Appearance
协调阶段(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 节点)。对于每个地点,它主要做两件事:
- "向下看" (Begin Phase -
beginWork
函数):- 协调算法(Reconciliation): React 会比较当前 Fiber 节点与对应的旧 Fiber 节点,检查
props
、state
、context
等是否发生变化。 - 子节点协调: 根据比较结果,决定子 Fiber 节点的处理策略:复用(bailout)、更新、创建或删除。
- 副作用标记: 如果检测到需要 DOM 操作,会在 Fiber 节点的
flags
字段上标记相应的副作用(如Placement
、Update
、Deletion
)。 - 性能优化: 通过
React.memo
、useMemo
、shouldComponentUpdate
等机制实现 bailout 优化,跳过不必要的子树协调。
- 协调算法(Reconciliation): React 会比较当前 Fiber 节点与对应的旧 Fiber 节点,检查
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);
// ... 其他组件类型
}
}
- "向上走" (Complete Phase -
completeWork
函数):- 当一个 Fiber 节点的所有子节点都处理完毕后,或者该节点本身没有子节点,React 开始执行
completeWork
。 - DOM 实例创建: 对于 Host 组件(如
<div>
、<span>
),如果是新创建的节点,会在此阶段创建对应的 DOM 实例。 - 属性处理: 处理和收集需要更新的 DOM 属性,为后续的 Commit 阶段做准备。
- 副作用收集: 将当前节点及其子树的副作用标记向上冒泡,构建副作用链表。
- Effect List 构建: 在 React 18 之前,会构建 Effect List;React 18+ 使用不同的副作用收集机制。
- 当一个 Fiber 节点的所有子节点都处理完毕后,或者该节点本身没有子节点,React 开始执行
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 已经:
- 构建了一个新的 Fiber 树 (work-in-progress tree),这个树代表了下一次要渲染的 UI 状态。
- 计算出了所有必要的 DOM 变更、生命周期调用等,并以副作用标记 (flags) 的形式记录在各个 Fiber 节点上。
- 生成了一个副作用列表 (effect list),按顺序排列了所有需要执行副作用的 Fiber 节点。
接下来,React 会进入 Commit 阶段,根据副作用列表来实际执行这些变更。
协调流程图
为了更直观地理解协调阶段的工作流程,下面提供详细的流程图:
流程说明
- 初始化阶段:设置
workInProgress
指向根 Fiber 节点 - 工作循环:持续处理工作单元,直到没有更多工作或需要让出控制权
- beginWork 阶段:处理当前节点,决定如何处理子节点
- 深度优先遍历:优先处理子节点,然后是兄弟节点,最后是父节点
- completeWork 阶段:完成当前节点的处理,收集副作用
- 并发控制:在每个工作单元后检查是否需要让出控制权
