react completework

8 阅读3分钟

如果说 beginWork 是从上到下**“递”的过程,负责计算和标记,那么 completeWork 就是从下到上“归”**的过程,负责收尾、创建 DOM 实例和收集副作用(Effects)。

completeWork 是在 performUnitOfWork 内部,当一个 Fiber 节点没有子节点时(即到达叶子节点,或者子节点已经全部处理完毕)开始执行的。

1. completeWork 的核心职责

completeWork 主要执行以下三个关键任务:

A. 创建/更新 DOM 节点 (Mounting & Update)

  • 如果是首次挂载 (Mounting)

    • 对于 Host Component(如 <div><span> 等),React 会调用宿主环境(Host Environment)的 API 来创建真实的 DOM 元素
    • 例如,处理 <div id="app"> 时,会执行类似 document.createElement('div') 的操作。
  • 如果是更新 (Updating)

    • React 会从 beginWork 阶段收集到的 Payload 中获取需要更新的属性(如 styleclassName 等)。
    • 它会将这些更新应用到已有的 DOM 节点上,但不会立即写入屏幕

B. 构建 Effect List(副作用链表)

这是 completeWork 最重要的工作之一,也被称为收集阶段(Collection Phase)

  • 当一个 Fiber 节点完成了它的工作(completeWork)后,它会检查自己、它的子节点以及所有子节点已经收集到的 Effects。
  • 它将这些带有副作用标记(如 PlacementUpdateDeletion 等)的 Fiber 节点,通过一个链表结构(称为 effectList)挂载到父级 Fiber 节点上。
  • 当这个收集过程一直回溯到 Fiber Root 时,整个 Root 上的 effectList 就包含了一次更新中所有需要执行的 DOM 操作生命周期/Hooks Effect

C. 处理 Props 和 Ref

  • Props:确保 DOM 元素上的 Props 能够正确应用,例如事件监听器等。
  • Ref:对于包含 ref 属性的组件,会在这个阶段将对应的 DOM 实例或组件实例引用附加到 ref 对象上,但这通常发生在 Commit 阶段

2. completeWork 的流程拆解

这个过程是回溯性的,从最深的子节点开始向根节点累积。

代码段

graph TD
    A[开始 completeWork current Fiber] --> B{节点类型?}
    
    %% 宿主组件路径
    B -- Host Component --> C1[创建或更新 DOM 元素]
    C1 --> C2[处理 DOM Props]
    C2 --> C3[将子 DOM 挂载到此 DOM 上 离屏构建]
    
    %% 函数/类组件路径
    B -- Class/Function Component --> D1[标记/处理 Layout Effects useLayoutEffect]
    D1 --> D2[标记/处理 Passive Effects useEffect]

    C3 --> E{检查是否有副作用 Flags?}
    D2 --> E
    
    E -- Yes --> F[将当前 Fiber 加入父级 Effect List]
    E -- No --> G[跳过此节点]
    
    F --> H[将子 Effect List 归并到父级 Effect List]
    G --> H
    
    H --> I[设置 nextUnitOfWork <br/>为兄弟节点或父节点]
    I --> End([完成 completeWork])

3. 与 beginWork 的关系

特性beginWork(递)completeWork(归)
时机从上往下遍历,遇到子节点就执行。从下往上回溯,子节点处理完毕后执行。
核心任务比较新旧 Fiber,执行组件函数,打上初步的副作用标记创建 DOM 节点,收集副作用,构建 Effect List。
产出物决定下一个要处理的 Fiber 节点(通常是子节点)。将 DOM 节点组装成离屏 DOM 树
执行次数每个 Fiber 节点执行一次。每个 Fiber 节点执行一次。

4. 最终结果:Effect List

workLoopConcurrent 循环结束时,completeWork 的结果是一个完整的 Fiber Root 上的 Effect List

  • 这个链表只包含那些真正需要修改(新增、更新或删除)的 DOM 节点或具有 Effects 的组件。
  • 在随后的 Commit 阶段,React 只需要遍历这个 Effect List,然后根据 Fiber 上的副作用标记(Flags)依次执行对应的 DOM 操作,这个过程是非常高效的,因为避免了遍历整棵树。

通过 completeWork,React 成功在内存中完成了所有准备工作,实现了 “Render 阶段的可中断性”“Commit 阶段的原子性”