Skip to content

协调阶段(Reconciliation Phase)概述

协调阶段(Reconciliation Phase)是 React 更新机制的心脏,它在内存中悄无声息地工作,找出界面(UI)从当前状态变到下一个状态所需要的最少操作。这个阶段的核心产出物是一个新的"设计蓝图"(work-in-progress Fiber 树),以及一份"施工清单"(副作用列表),告诉后续的提交阶段(Commit Phase)具体要做哪些 DOM 修改或执行哪些附带操作。

核心概念与术语

在深入了解协调阶段之前,让我们先明确一些关键术语:

  • Fiber 树:React 内部用于表示组件层次结构的数据结构
  • work-in-progress 树:正在构建中的新 Fiber 树
  • 副作用(Side Effects):需要在 DOM 上执行的操作,如插入、更新、删除节点
  • 副作用列表(Effect List):包含所有需要执行副作用的 Fiber 节点的链表
  • 工作单元(Unit of Work):协调过程中处理的最小单位,通常是一个 Fiber 节点

工作循环

当调度器(Scheduler)确定当前更新任务具有足够的优先级并且浏览器有可用的时间片时,协调阶段就正式启动它的核心工作循环(workLoop)。这个过程就像是在一个巨大的城市地图(Fiber 树)上寻路和规划。

javascript
// React 19 工作循环的简化版本
function workLoop() {
  while (workInProgress !== null && !shouldYield()) {
    performUnitOfWork(workInProgress);
  }
}

function performUnitOfWork(unitOfWork) {
  const next = beginWork(unitOfWork);
  if (next === null) {
    completeUnitOfWork(unitOfWork);
  } else {
    workInProgress = next;
  }
}

React 会从应用的根节点开始,采用"深度优先"的策略,一个一个地"访问"地图上的每个地点(Fiber 节点)。对于每个地点,它主要做两件事:

  1. "向下看" (Begin Phase - beginWork 函数):
    • 协调算法(Reconciliation): React 会比较当前 Fiber 节点与对应的旧 Fiber 节点,检查 propsstatecontext 等是否发生变化。
    • 子节点协调: 根据比较结果,决定子 Fiber 节点的处理策略:复用(bailout)、更新、创建或删除。
    • 副作用标记: 如果检测到需要 DOM 操作,会在 Fiber 节点的 flags 字段上标记相应的副作用(如 PlacementUpdateDeletion)。
    • 性能优化: 通过 React.memouseMemoshouldComponentUpdate 等机制实现 bailout 优化,跳过不必要的子树协调。
javascript
// beginWork 的简化逻辑
function beginWork(current, workInProgress, renderLanes) {
  // 检查是否可以复用当前节点
  if (current !== null && !didReceiveUpdate) {
    return bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes);
  }
  
  // 根据组件类型进行不同的处理
  switch (workInProgress.tag) {
    case FunctionComponent:
      return updateFunctionComponent(current, workInProgress, renderLanes);
    case ClassComponent:
      return updateClassComponent(current, workInProgress, renderLanes);
    case HostComponent:
      return updateHostComponent(current, workInProgress, renderLanes);
    // ... 其他组件类型
  }
}
  1. "向上走" (Complete Phase - completeWork 函数):
    • 当一个 Fiber 节点的所有子节点都处理完毕后,或者该节点本身没有子节点,React 开始执行 completeWork
    • DOM 实例创建: 对于 Host 组件(如 <div><span>),如果是新创建的节点,会在此阶段创建对应的 DOM 实例。
    • 属性处理: 处理和收集需要更新的 DOM 属性,为后续的 Commit 阶段做准备。
    • 副作用收集: 将当前节点及其子树的副作用标记向上冒泡,构建副作用链表。
    • Effect List 构建: 在 React 18 之前,会构建 Effect List;React 18+ 使用不同的副作用收集机制。
javascript
// completeWork 的简化逻辑
function completeWork(current, workInProgress, renderLanes) {
  switch (workInProgress.tag) {
    case HostComponent: {
      const type = workInProgress.type;
      if (current !== null && workInProgress.stateNode != null) {
        // 更新现有的 DOM 节点
        updateHostComponent(current, workInProgress, type);
      } else {
        // 创建新的 DOM 节点
        const instance = createInstance(type, workInProgress.pendingProps);
        appendAllChildren(instance, workInProgress);
        workInProgress.stateNode = instance;
      }
      break;
    }
    case FunctionComponent:
    case ClassComponent:
      // 函数组件和类组件通常不需要特殊处理
      break;
  }
  return null;
}

这个"向下看"再"向上走"的过程会持续进行,直到整个地图(Fiber 树)都规划完毕。

协调阶段的完成

当工作循环处理完 Root Fiber 的 completeWork 后,整个协调阶段(Render Phase)就结束了。此时,React 已经:

  1. 构建了一个新的 Fiber 树 (work-in-progress tree),这个树代表了下一次要渲染的 UI 状态。
  2. 计算出了所有必要的 DOM 变更、生命周期调用等,并以副作用标记 (flags) 的形式记录在各个 Fiber 节点上。
  3. 生成了一个副作用列表 (effect list),按顺序排列了所有需要执行副作用的 Fiber 节点。

接下来,React 会进入 Commit 阶段,根据副作用列表来实际执行这些变更。

协调流程图

为了更直观地理解协调阶段的工作流程,下面提供详细的流程图:

流程说明

  1. 初始化阶段:设置 workInProgress 指向根 Fiber 节点
  2. 工作循环:持续处理工作单元,直到没有更多工作或需要让出控制权
  3. beginWork 阶段:处理当前节点,决定如何处理子节点
  4. 深度优先遍历:优先处理子节点,然后是兄弟节点,最后是父节点
  5. completeWork 阶段:完成当前节点的处理,收集副作用
  6. 并发控制:在每个工作单元后检查是否需要让出控制权

微信公众号二维码