目标:
-
理解 beginWork、completeWork 的作用
-
搞懂调和的核心流程
推荐文件:
-
ReactFiberBeginWork.js
-
ReactFiberCompleteWork.js
-
ReactChildFiber.js
理解 beginWork、completeWork 的作用
在 React 源码中,beginWork
和 completeWork
是 调和(reconciliation)阶段的核心函数,它们构成了 Fiber 工作流程的前后两步,分别处理「开始」和「完成」的逻辑。
总体理解
React 构建 Fiber 树(工作单元)的过程是通过一个 “双缓冲 Fiber 架构” 来实现的:
-
beginWork
:创建/更新子 Fiber 节点 ——「拆分工作单元」 -
completeWork
:根据子节点完成当前节点 ——「归并工作结果」
你可以类比为:
beginWork:我需要干什么?
completeWork:我已经干完了,汇总下结果。
🔍 一、beginWork
:从上往下,调和子节点
function beginWork(current, workInProgress, renderLanes): Fiber | null
-
current
是旧的 Fiber 节点(可能是 null) -
workInProgress
是正在构建的新 Fiber 节点 -
它会:
-
根据
workInProgress
的type
(函数组件、类组件、DOM 节点等) -
调用对应的更新函数(如
updateFunctionComponent
,updateHostComponent
等) -
创建并返回它的第一个子节点(继续下一轮 beginWork)
-
📌 关键职责:
-
比较 props 是否变化,决定是否跳过更新
-
根据组件类型,处理 state、effect
-
创建子 Fiber(
reconcileChildren
)
🛠 例子:
function updateFunctionComponent(...) {
const children = renderWithHooks(...) // 执行函数组件逻辑
reconcileChildren(...) // 生成子 fiber 链表
return workInProgress.child // 进入下一个 beginWork
}
🔄 二、completeWork
:从下往上,生成副作用/最终输出
function completeWork(current, workInProgress, renderLanes)
-
与
beginWork
相对,从 子节点回到父节点 -
汇总信息,比如:
-
生成 DOM 节点(
createInstance
) -
记录副作用(flags: Placement/Update/Deletion)
-
设置
stateNode
-
绑定 DOM 属性等
-
📌 关键职责:
-
创建/更新 DOM(或其他宿主环境节点)
-
链接兄弟节点
-
收集副作用(flags)
🛠 例子:
function completeWork(current, workInProgress) {
switch (workInProgress.tag) {
case HostComponent:
if (!current) {
// 首次挂载,创建 DOM 节点
const instance = createInstance(...)
appendAllChildren(instance, workInProgress)
workInProgress.stateNode = instance
}
break
}
}
🧭 三、beginWork 与 completeWork 的配合流程(深度优先)
调和过程是 DFS 的:
beginWork(A)
beginWork(B)
beginWork(C)
completeWork(C)
completeWork(B)
completeWork(A)
这就是为啥你会看到 React 的调和是“从父到子,再从子归并到父”。
阶段 | 函数名 | 作用 | 类比 |
---|---|---|---|
开始(向下) | beginWork | 调和子节点,生成子 Fiber | 分解任务 |
完成(向上) | completeWork | 汇总子节点结果,创建 DOM 或收集副作用 | 任务收尾 |
✅ 你在源码中如何追踪?
-
从
performUnitOfWork
开始:
function performUnitOfWork(unitOfWork: Fiber) {
const next = beginWork(current, unitOfWork, renderLanes)
...
if (!next) completeUnitOfWork(unitOfWork)
}
如何本地调试代码
下载一个react的版本,我用的是18.3.0的版本,然后使用node16,执行了yarn install,安装了依赖,然后执行下面的打包命令,不要用最新的19版本,打包后的文件目录不同了
# 执行打包命令
yarn build react/index,react/jsx,react-dom/index,scheduler --type=NODE
现在源码目录build/node_modules
下会生成最新代码的包。我们为react
、react-dom
创建yarn link
。
通过
yarn link
可以改变项目中依赖包的目录指向
cd build/node_modules/react
# 申明react指向
yarn link
cd build/node_modules/react-dom
# 申明react-dom指向
yarn link
接下来我们通过create-react-app
在其他地方创建新项目。这里我们随意起名,比如“react-test”。
npx create-react-app react-test
在新项目中,将react
与react-dom
2个包指向react18.3.0
下我们刚才生成的包。
# 将项目内的react react-dom指向之前申明的包
yarn link react react-dom
现在试试在react/build/node_modules/react-dom/cjs/react-dom.development.js
中随意打印些东西。
在react-test
项目下执行yarn start
。现在浏览器控制台已经可以打印出我们输入的东西了。
搞懂调和的核心流程
一句话理解调和(Reconciliation)
调和是 React 在更新阶段用 Fiber 树对比新旧 Virtual DOM,并计算出最小变更的过程,最终生成“Effect List”供 commit 阶段使用。
学习调和核心流程的完整路径
🥇 第一步:理解 Fiber 架构的核心概念
概念 | 说明 |
---|---|
Fiber 节点 | 包含组件信息的工作单元,代替以前的 Virtual DOM |
Work Loop | 整个调和阶段的遍历引擎,按时间分片、可中断 |
beginWork | 每个 Fiber 的“构建”阶段:计算子节点 |
completeWork | 每个 Fiber 的“收尾”阶段:生成 DOM 或 effect |
Effect List | 记录需要执行副作用的 Fiber 链表,提交阶段用 |
commitWork | 真正执行副作用,比如操作 DOM、调用生命周期等 |
第二步:亲手调试源码
推荐调试入口是 ReactDOMServer.renderToString()
,触发构建 Fiber 树并走完整个调和流程。
推荐打断点的文件:
-
ReactFiberWorkLoop.new.js
:调和主循环workLoopSync
-
ReactFiberBeginWork.new.js
:beginWork
逻辑 -
ReactFiberCompleteWork.new.js
:completeWork
逻辑 -
ReactChildFiber.js
:reconcileChildren
是调和的核心函数之一 -
ReactFiberReconciler.js
:暴露外部接口,如createContainer
、updateContainer
第三步:自己写一个极简调和器(mini reconciler)
function beginWork(fiber) {
if (typeof fiber.type === 'function') {
fiber.child = fiber.type(fiber.props);
} else {
fiber.stateNode = createDom(fiber);
}
return fiber.child;
}
function completeWork(fiber) {
if (fiber.return) {
fiber.return.effects = fiber.return.effects || [];
fiber.return.effects.push(fiber);
}
}
function workLoop(rootFiber) {
let next = rootFiber;
while (next) {
let child = beginWork(next);
if (child) {
next = child;
} else {
while (next) {
completeWork(next);
if (next.sibling) {
next = next.sibling;
break;
}
next = next.return;
}
}
}
}
总结你该掌握的关键流程
-
从 React 元素(JSX)转成 Fiber 节点
-
通过 beginWork 构建子 Fiber(递)
-
通过 completeWork 完成当前 Fiber(归)
-
收集 effect 到 EffectList
-
commitWork 真正执行 DOM 操作