React中BeginWork和CompleteWork解析

React中BeginWork和CompleteWork核心解析

一、前置认知

beginWork 和 completeWork 是 React 调和阶段(Reconciliation) 的两大核心工作单元,基于Fiber架构深度优先遍历执行,共同完成[Fiber树构建/更新、Diff算法执行、DOM节点生成、副作用收集]的核心逻辑,为提交阶段提供可执行的指令与依赖。

  • 调和阶段 :属于React渲染的可中断、可恢复阶段(由Scheduler调度),只计算更新结果,不操作实际的DOM元素。
  • Fiber 遍历规则:先向下执行 beginWork(递)→ 叶子节点后向上执行completeWork(归),单节点遍历完成 = beginWork 执行完毕 + completeWork 执行完毕。

二、beginWork 核心解析

  1. 核心职责(Fiber 构建 / 更新 + Diff 核心)

    beginWork是[递]阶段的核心方法,从根Fiber开始向下遍历,为每个Fiber节点执行创建、更新逻辑。核心做以下4件事:

    1. Fiber节点创建/复用: 根据当前组件类型(类组件、函数组件、原生DOM),创建新的Fiber或复用已有Fiber(首屏渲染创建,更新阶段则复用);
    2. Diff算法核心执行 :更新阶段对比「当前 Fiber」和「新虚拟 DOM(pending)」,计算出节点的增 / 删 / 改差异,生成待更新的子 Fiber 节点
    3. 子Fiber树构建:为当前 Fiber 生成 / 更新子 Fiber 节点,建立 Fiber 树的父子关联,为后续遍历做准备;
    4. 优先级判断:结合 Scheduler 优先级机制,判断当前 Fiber 节点的更新是否需要中断,保证高优先级任务(如用户交互)优先执行。
  2. 核心执行逻辑(分首屏 / 更新阶段)

    beginWork 会根据当前 Fiber 树的状态(首屏渲染 / 更新)走不同逻辑,核心是区分首次构建和 Diff 更新,避免无意义的计算:

  • 首屏渲染(mount):无旧 Fiber 节点,直接根据虚拟 DOM 创建全新的 Fiber 节点,设置节点类型、属性、子节点等信息;
  • 更新阶段(update):存在旧 Fiber 节点,执行Diff 算法(组件级 Diff + 元素级 Diff),复用可复用的旧 Fiber,标记需要删除 / 更新的节点,创建新增节点的 Fiber。

三、completeWork 核心解析

  1. 核心职责(DOM 生成 + 副作用收集 + 属性处理)

    completeWork是「归阶段」的核心方法,从 Fiber 树的叶子节点开始向上遍历,为每个 Fiber 节点执行收尾工作 **,核心做 4 件事:

    1. 真实 DOM 节点生成 / 挂载:仅针对原生 DOM 类型 Fiber(如 div/span),创建真实 DOM 元素,设置节点属性(className/style/attr 等),并将子 DOM 节点挂载到父 DOM 上;
    2. 组件属性 / 上下文处理:处理类组件的 props 透传、上下文(Context)的传递与更新,保证组件层级的属性一致性;
    3. 副作用收集:将当前 Fiber 节点的更新操作(如 DOM 增删改、生命周期执行、useEffect 回调)标记为副作用(Effect),并加入到全局副作用队列中,供提交阶段执行;
    4. Fiber 节点收尾:完善 Fiber 节点的附加信息,建立 Fiber 与真实 DOM 的映射关系(stateNode 属性),为后续更新和查找提供依据。
  2. 关键特性

  • 仅原生 DOM Fiber会创建真实 DOM,组件类型 Fiber(类 / 函数)不直接生成 DOM,仅做逻辑处理;
  • 副作用收集是提交阶段执行真实操作的基础,调和阶段不执行任何副作用,仅标记;
  • 向上遍历过程中,会将子 Fiber 的副作用合并到父 Fiber,最终根 Fiber 持有整个应用的所有副作用,方便提交阶段统一处理。

四、调和过程(beginWork + completeWork)伪代码

js 复制代码
/**
 * React 调和阶段核心遍历方法(深度优先)
 * @param {Fiber} currentFiber - 旧Fiber节点(更新阶段)/null(首屏渲染)
 * @param {VNode} pendingVNode - 新虚拟DOM节点
 * @param {number} expirationTime - 任务过期时间(Scheduler 优先级)
 * @returns {Fiber} 处理后的新Fiber节点
 */
function reconcileFiber(currentFiber, pendingVNode, expirationTime) {
  // 1. 执行 beginWork:递阶段 - 构建/更新Fiber + Diff逻辑
  const nextFiber = beginWork(currentFiber, pendingVNode, expirationTime);

  // 2. 深度优先遍历:若有子Fiber,递归处理子节点(继续递阶段)
  if (nextFiber.child) {
    reconcileFiber(null, nextFiber.child.pendingVNode, expirationTime);
  }

  // 3. 执行 completeWork:归阶段 - 生成DOM + 收集副作用
  completeWork(nextFiber);

  return nextFiber;
}

/**
 * beginWork 核心方法:递阶段 - Fiber构建/Diff/子树生成
 * @param {Fiber} current - 旧Fiber(update)/null(mount)
 * @param {VNode} vnode - 新虚拟DOM
 * @param {number} expirationTime - 优先级过期时间
 * @returns {Fiber} 新/更新后的Fiber节点
 */
function beginWork(current, vnode, expirationTime) {
  let nextFiber;
  const { type, props } = vnode;

  // 核心职责1:判断更新类型 - 首屏(mount) / 更新(update)
  const isMount = !current;

  if (isMount) {
    // 首屏渲染:核心职责2 - 创建全新Fiber节点
    nextFiber = createFiber(vnode, expirationTime);
  } else {
    // 更新阶段:核心职责3 - 执行Diff算法,复用/更新旧Fiber
    nextFiber = reconcileUpdate(current, vnode, expirationTime);
  }

  // 核心职责4:构建子Fiber树 - 生成子节点Fiber,建立父子关联
  nextFiber.child = createChildFibers(nextFiber, props.children, expirationTime);

  // 核心职责5:优先级判断 - 若有更高优先级任务,标记中断
  if (shouldYield(expirationTime)) {
    markWorkInProgress(nextFiber); // 标记为进行中,后续可恢复
  }

  return nextFiber;
}

/**
 * completeWork 核心方法:归阶段 - DOM生成/副作用收集/属性处理
 * @param {Fiber} fiber - 已完成beginWork的Fiber节点
 */
function completeWork(fiber) {
  const { type, props, stateNode } = fiber;
  const isHostComponent = isHostDOMType(type); // 是否为原生DOM组件(div/span等)

  // 核心职责1:原生DOM组件 - 生成/更新真实DOM节点
  if (isHostComponent) {
    if (!stateNode) {
      // 首屏:创建真实DOM,挂载到Fiber的stateNode(Fiber-DOM映射)
      fiber.stateNode = createDOMElement(type, props);
    } else {
      // 更新:执行DOM属性Diff,更新已有DOM(避免全量替换)
      updateDOMAttributes(stateNode, props);
    }
    // 核心职责2:将子DOM节点挂载到当前父DOM(归阶段向上,子DOM已生成)
    appendChildDOM(fiber.stateNode, fiber.child?.stateNode);
  }

  // 核心职责3:处理组件上下文/属性透传(类组件/Context)
  propagateContext(fiber);
  resolveProps(fiber);

  // 核心职责4:收集副作用 - 根据Fiber状态标记对应的Effect(DOM增/删/改/生命周期等)
  if (hasSideEffect(fiber)) {
    collectEffect(fiber, fiber.effectTag); // 加入全局副作用队列
  }

  // 核心职责5:Fiber收尾 - 合并子Fiber的副作用,向上传递
  if (fiber.sibling) {
    fiber.return.effects = mergeEffects(fiber.return.effects, fiber.effects);
  }
}

// 判断是否为原生DOM类型组件
function isHostDOMType(type) {
  return typeof type === 'string' && ['div', 'span', 'p', 'input'].includes(type);
}
// 判断是否需要中断(Scheduler 时间切片/高优先级抢占)
function shouldYield(expirationTime) {
  return performance.now() >= expirationTime || hasHigherPriorityWork();
}

五、核心职责标注(分阶段 / 分方法)

1. beginWork(递阶段)核心职责
职责编号 具体职责 适用场景 核心目的
1 区分首屏 (mount)/ 更新 (update) 所有 Fiber 节点 避免无意义的 DOM 操作 / Diff 计算
2 首屏渲染创建全新 Fiber 节点 mount 阶段 构建初始 Fiber 树
3 更新阶段执行 Diff 算法 update 阶段 计算节点增 / 删 / 改差异
4 生成子 Fiber 节点,构建子树 所有 Fiber 节点 为深度优先遍历提供后续节点
5 优先级判断与工作中断标记 所有 Fiber 节点 适配 Scheduler 可中断调度
2. completeWork(归阶段)核心职责
职责编号 具体职责 适用场景 核心目的
1 原生 DOM 组件创建真实 DOM 宿主组件(div/span 等) 映射 Fiber 到真实 DOM
2 原生 DOM 组件更新属性 / Diff 宿主组件 update 阶段 最小化 DOM 操作
3 子 DOM 节点挂载到父 DOM 宿主组件 构建完整的真实 DOM 树
4 处理 Context/Props 透传 类组件 / Context 相关 保证组件层级数据一致性
5 收集当前 Fiber 的副作用 有更新的 Fiber 节点 为 Commit 阶段提供执行指令
6 合并子 Fiber 副作用并向上传递 非叶子节点 Fiber 根 Fiber 统一管理所有副作用

六、关键执行流程(深度优先遍历示例)

以简单 DOM 结构 <div><p>React</p></div> 为例,展示 beginWork + completeWork 的执行顺序:

核心规律:先父后子执行 beginWork,先子后父执行 completeWork,叶子节点是第一个完成 beginWork + completeWork 的节点。

七、与 Fiber 架构 / 调度器的关联

  1. 可中断性:beginWork 执行过程中,若 Scheduler 判定需要中断(如 5ms 时间切片到期、高优先级任务抢占),会立即停止遍历,将当前未完成的 Fiber 标记为「工作中」,后续恢复时从该节点继续执行;
  2. 副作用隔离:调和阶段仅在 completeWork 中收集副作用,不执行任何真实操作(如 DOM 修改、生命周期调用),所有副作用统一在 Commit 阶段执行,保证更新的原子性;
  3. Fiber 映射:completeWork 为原生 DOM Fiber 建立 stateNode 映射(Fiber.stateNode = 真实 DOM),这是 React 后续操作 DOM 的核心依据,也是 Diff 算法能精准更新的基础。
相关推荐
_下雨天.2 小时前
HAProxy搭建Web群集
前端
梦想CAD控件2 小时前
在线CAD开发包图纸转换功能使用指南
前端·javascript·vue.js
亚空间仓鼠2 小时前
Ansible之Playbook(三):变量应用
java·前端·ansible
invicinble2 小时前
前端技术栈整理
前端
倾颜3 小时前
pnpm monorepo 下,如何把 Next.js 应用里的稳定内核拆成内部 workspace 包
前端·react.js·next.js
念格3 小时前
Flutter 仿微信输入框最佳实践:自适应高度 + 超行数智能切换全屏
前端·flutter
GISer_Jing3 小时前
前端图片、动图与动画全解析(含PNG/APNG/Lottie/GIF/Canvas/WebGL/WebGPU)
前端·3d·动画·webgl
OpenTiny社区3 小时前
多端开发头疼?TinyVue 3.30 一招搞定,AI还帮你写代码!
前端·vue.js·github
ZHENGZJM3 小时前
前端认证状态管理与路由守卫
前端·状态模式