《深入浅出react》总结之 10.2 渲染阶段流程探秘-completeWork

让我们再回顾一下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;就这样一直深度优先遍历到叶子结点,直接看下图:

graph TD A[workLoopSync 启动] --> B{workInProgress 非空?} B -->|是| C[调用 performUnitOfWork] C --> D[执行 beginWork] D --> E{beginWork 返回
子节点 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[结束]
  1. 子节点获取策略

    javascript 复制代码
    // 简化版 beginWork 逻辑
    function beginWork(current, workInProgress) {
      switch (workInProgress.tag) {
        case FunctionComponent:
          return updateFunctionComponent(current, workInProgress);
        case HostComponent:
          return updateHostComponent(current, workInProgress);
        // ...其他类型处理
      }
    }
  2. 无子节点时的回溯

  • beginWork 返回 null 时触发 completeUnitOfWork
  • 优先检查兄弟节点 (workInProgress.sibling)
  • 无兄弟节点时回溯父节点,直到根节点

📌 重要特征

  1. 不可逆流程
    workInProgress 指针一旦离开父节点就不会再回到该节点(直到后续提交阶段)

  2. 节点复用机制
    beginWork 通过current树(当前UI树)复用Fiber节点,减少创建开销

  3. 副作用标记

    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

graph TD A["开始completeUnitOfWork"] --> B["当前节点:completedWork = unitOfWork"] B --> C["执行completeWork处理当前节点"] C --> D{"检查兄弟节点siblingFiber是否存在?"} D -->|存在| E["workInProgress = siblingFiber
结束当前回溯"] 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 的流程 }
}
graph TD A["开始 completeWork"] --> B["获取 newProps"] B --> C{"根据 tag 分支"} C --> |ClassComponent| D["调用 bubbleProperties"] D --> Z["返回 null"] C --> |HostComponent| E{"是更新流程?"} E --> |是| F["updateHostComponent"] F --> G{"ref 变化?"} G --> |是| H["markRef"] G --> |否| I E --> |否| J{"需要注水?"} J --> |是| K["服务端渲染"] J --> |否| L["createInstance"] L --> M["appendAllChildren"] H --> I I --> N["调用 bubbleProperties"] K --> N M --> N N --> Z C --> |其他| X["其他处理"] X --> Z

这里留意一个重点:bubbleProperties -- 设置 subtreeFlags,

还记得 我们之前介绍过的 childLanes 吗?从更新的子组件一直向上标记 childLanes,方便寻找真正需要更新的子组件

其实 subtreeFlags 和 childLanes是异曲同工之妙,只不过 subtreeFlags标记子树中的副作用操作,方便 commit 阶段快速处理

🧩 设计哲学统一性

设计原则 subtreeFlags实现 childLanes实现
最小化工作范围 通过副作用的位掩码标识 通过优先级车道标识
树状结构聚合 都采用自底向上的状态聚合,父节点聚合子树的副作用状态
增量更新支持 识别哪些子树需要DOM操作 识别哪些子树需要协调过程
高效状态传播 位运算快速合并状态 位运算快速合并优先级
并发模式基础 提交阶段安全应用副作用 协调阶段支持任务中断与恢复

总结

下面这两句话,请你背下来,并且是充分理解后背下来!

beginWork:找到发生更新的类组件,执行类组件和函数组件获得新 element,diff 生成新的 fiber。根据不同类型的 fiber,处理不同的逻辑。fiber 中有需要更新的地方,打上标志。

completeWork:根据子代 fiber 的标志,形成父级的 subtreeFlags 属性。初始化阶段会创建元素,建立 DOM 树结构

相关推荐
G等你下课15 分钟前
告别刷新就丢数据!localStorage 全面指南
前端·javascript
该用户已不存在16 分钟前
不知道这些工具,难怪的你的Python开发那么慢丨Python 开发必备的6大工具
前端·后端·python
爱编程的喵19 分钟前
JavaScript闭包实战:从类封装到防抖函数的深度解析
前端·javascript
LovelyAqaurius19 分钟前
Unity URP管线着色器库攻略part1
前端
Xy91022 分钟前
开发者视角:App Trace 一键拉起(Deep Linking)技术详解
java·前端·后端
lalalalalalalala24 分钟前
开箱即用的 Vue3 无限平滑滚动组件
前端·vue.js
前端Hardy24 分钟前
8个你必须掌握的「Vue」实用技巧
前端·javascript·vue.js
hxmmm25 分钟前
react合成事件
react.js
snakeshe101027 分钟前
深入理解 React 中 useEffect 的 cleanUp 机制
前端
星月日28 分钟前
深拷贝还在用lodash吗?来试试原装的structuredClone()吧!
前端·javascript