让我们再回顾一下performUnitOfWork 的过程
js
function performUnitOfWork(unitOfWork){
/* 执行 */
let next = beginWork(current, unitOfWork, subtreeRenderLanes);
unitOfWork.memoizedProps = unitOfWork.pendingProps;
/* 优先将 next 赋值给 workInProgress,如果没有 next,那么调用 completeUnitOfWork 向上归并处理。 */
if (next = = = null) {
completeUnitOfWork(unitOfWork);
} else {
workInProgress = next;
}
}
performUnitOfWork 是什么来着?他是调和每一个 workInProgress fiber的入口,workInProgress只要不为空,就会一直执行下去
js
function workLoopSync() {
/* 循环执行 performUnitOfWork,一直到 workInProgress 为空 */
while (workInProgress ! = = null) {
performUnitOfWork(workInProgress);
}
}
好,那么 workInProgress fiber 是怎么流转的,就是通过 beginWork() 的返回值,执行 beginWork()会返回 workInProgress fiber的子fiber,再执行 workInProgress = next;就这样一直深度优先遍历到叶子结点,直接看下图:
子节点 next?} E -->|返回子节点| F[workInProgress = next] F --> B E -->|无子节点| G[completeUnitOfWork] G --> H{有兄弟节点?} H -->|是| I[workInProgress = 兄弟节点] I --> B H -->|无| J[回溯父节点完成] J --> G B -->|workInProgress=null| K[结束]
-
子节点获取策略 :
javascript// 简化版 beginWork 逻辑 function beginWork(current, workInProgress) { switch (workInProgress.tag) { case FunctionComponent: return updateFunctionComponent(current, workInProgress); case HostComponent: return updateHostComponent(current, workInProgress); // ...其他类型处理 } }
-
无子节点时的回溯:
- 当
beginWork
返回null
时触发completeUnitOfWork
- 优先检查兄弟节点 (
workInProgress.sibling
) - 无兄弟节点时回溯父节点,直到根节点
📌 重要特征
-
不可逆流程
workInProgress
指针一旦离开父节点就不会再回到该节点(直到后续提交阶段) -
节点复用机制
beginWork
通过current
树(当前UI树)复用Fiber节点,减少创建开销 -
副作用标记
在
beginWork
过程中会标记节点的副作用(如增删改)-- flag标记--记住这里,一会要考
completeWork 阶段
先看代码
js
function completeUnitOfWork(unitOfWork){
let completedWork = unitOfWork;
do {
/* 向上找到父级 fiber */
const current = completedWork.alternate;
const returnFiber = completedWork.return;
/* 执行 completeWork */
completeWork(current, completedWork, subtreeRenderLanes);
const siblingFiber = completedWork.sibling;
/* 当前 fiber 如果有兄弟节点,那么停止循环,将当前兄弟节点赋值给 workInProgress,然后这个节点会进入接下来的 workLoop 中。*/
if (siblingFiber ! = = null) {
workInProgress = siblingFiber;
return;
}
completedWork = returnFiber;
workInProgress = completedWork;
}while (completedWork! = = null);
}
首先来看流程:
1.会先让叶子结点fiber 执行completeWork,
2.当前叶子 fiber 如果有兄弟节点,则停止循环,将当前兄弟节点赋给 workInProgress,然后这个节点会退出completeUnitOfWork流程,进入下一个performUnitOfWork中,进入接下来的 workLoop 中。
3.如果没有兄弟,就把 completedWork 指向 叶子结点的父fiber,继续让 父fiber 执行 completeWork
结束当前回溯"] D -->|不存在| F["completedWork = returnFiber
workInProgress = returnFiber"] F --> G{"returnFiber为null?"} G -->|否| C G -->|是| H["结束整个工作循环"] classDef highlight fill:#ffcc00,stroke:#333; class E highlight;
核心流程completeWork
还是上代码
js
function completeWork(current,workInProgress,renderLanes){
const newProps = workInProgress.pendingProps;
switch (workInProgress.tag) {
/* 如果是类组件,那么执行 bubbleProperties */
case ClassComponent: {
bubbleProperties(workInProgress);
return null;
}
/* DOM 元素 */
case HostComponent: {
if (current ! = = null && workInProgress.stateNode! = null) {
/* 更新流程 */
updateHostComponent(current,workInProgress,type,newProps,rootContainerInstance)
if (current.ref! = = workInProgress.ref) {
/* 当 ref 变化,会重新标记 ref */
markRef(workInProgress);
}
}else{ /* 初始化流程 */
if (wasHydrated) {
// 服务端渲染,这里暂时不考虑 }else{
/* 创建 DOM 元素 */
const instance = createInstance(type,newProps,rootContainerInstance,currentHostContext,workInProgress,);
/* 插入真实的 DOM 元素 */
appendAllChildren(instance, workInProgress, false, false);
}
}
bubbleProperties(workInProgress);
return null;
}
// ...省略其他 fiber 的流程 }
}
这里留意一个重点:bubbleProperties -- 设置 subtreeFlags,
还记得 我们之前介绍过的 childLanes 吗?从更新的子组件一直向上标记 childLanes,方便寻找真正需要更新的子组件
其实 subtreeFlags 和 childLanes是异曲同工之妙,只不过 subtreeFlags标记子树中的副作用操作,方便 commit 阶段快速处理
🧩 设计哲学统一性
设计原则 | subtreeFlags实现 | childLanes实现 |
---|---|---|
最小化工作范围 | 通过副作用的位掩码标识 | 通过优先级车道标识 |
树状结构聚合 | 都采用自底向上的状态聚合,父节点聚合子树的副作用状态 |
|
增量更新支持 | 识别哪些子树需要DOM操作 | 识别哪些子树需要协调过程 |
高效状态传播 | 位运算快速合并状态 | 位运算快速合并优先级 |
并发模式基础 | 提交阶段安全应用副作用 | 协调阶段支持任务中断与恢复 |
总结
下面这两句话,请你背下来,并且是充分理解后背下来!
beginWork:找到发生更新的类组件,执行类组件和函数组件获得新 element,diff 生成新的 fiber。根据不同类型的 fiber,处理不同的逻辑。fiber 中有需要更新的地方,打上标志。
completeWork:根据子代 fiber 的标志,形成父级的 subtreeFlags 属性。初始化阶段会创建元素,建立 DOM 树结构