从源码角度浅析react19fiber树构建流程

137 阅读12分钟

一. 前置概要

  1. performWorkOnRoot
    这是根节点调度的入口函数,负责驱动一次完整的渲染流程。

  2. renderRootConcurrent / renderRootSync
    根据调度策略,选择并发或同步渲染,内部会循环调用 performUnitOfWork。

  3. performUnitOfWork
    这是 Fiber 树遍历的核心,每次处理一个 Fiber 节点,并调用 beginWork。

  4. beginWork
    这里会根据 Fiber 类型(函数组件、类组件、HostComponent 等)生成子 Fiber 节点,递归构建 Fiber 树。

总结:
Fiber 树的构建入口是 performWorkOnRoot,真正的 Fiber 节点构建发生在 beginWork
performWorkOnRoot → renderRootConcurrent/renderRootSync → performUnitOfWork → beginWork(递归构建子 Fiber)

二. 具体分析

performWorkOnRoot

React Fiber 协调器的核心入口之一,负责在根节点(root)上执行一次完整的渲染(render)和提交(commit)流程。它会根据当前的优先级、调度策略和渲染状态,选择同步或并发的渲染方式,并处理各种异常、挂起(suspend)、错误恢复等复杂场景

源码如下:

export function performWorkOnRoot(
  root: FiberRoot,
  lanes: Lanes,
  forceSync: boolean,
): void {
  // 1. 防止嵌套渲染或提交,保证执行上下文正确
  if ((executionContext & (RenderContext | CommitContext)) !== NoContext) {
    throw new Error('Should not already be working.');
  }

  // 2. 性能分析:如有必要,记录上次 yield 的耗时
  if (enableProfilerTimer && enableComponentPerformanceTrack) {
    if (workInProgressRootRenderLanes !== NoLanes && workInProgress !== null) {
      const yieldedFiber = workInProgress;
      // 记录让出主线程的耗时和原因
      const yieldEndTime = now();
      switch (yieldReason) {
        case SuspendedOnImmediate:
        case SuspendedOnData:
          logSuspendedYieldTime(yieldStartTime, yieldEndTime, yieldedFiber);
          break;
        case SuspendedOnAction:
          logActionYieldTime(yieldStartTime, yieldEndTime, yieldedFiber);
          break;
        default:
          logYieldTime(yieldStartTime, yieldEndTime);
      }
    }
  }

  // 3. 判断是否启用时间分片(并发渲染),否则走同步渲染
  const shouldTimeSlice =
    (!forceSync &&
      !includesBlockingLane(lanes) &&
      !includesExpiredLane(root, lanes)) ||
    // prerender 场景下即使是同步优先级也走并发渲染,避免阻塞主线程
    checkIfRootIsPrerendering(root, lanes);

  // 4. 根据 shouldTimeSlice 选择并发或同步渲染主流程
  let exitStatus = shouldTimeSlice
    ? renderRootConcurrent(root, lanes)
    : renderRootSync(root, lanes, true);

  let renderWasConcurrent = shouldTimeSlice;

  // 5. 渲染主循环,处理一致性校验、错误重试、挂起等特殊情况
  do {
    if (exitStatus === RootInProgress) {
      // 渲染未完成,可能因为 prerender/suspend 等原因需要暂停
      if (workInProgressRootIsPrerendering && !shouldTimeSlice) {
        // prerender 模式下但未启用时间分片,需标记为挂起
        const didAttemptEntireTree = false;
        markRootSuspended(root, lanes, NoLane, didAttemptEntireTree);
      }
      if (enableProfilerTimer && enableComponentPerformanceTrack) {
        // 记录 yield 的原因和时间
        startYieldTimer(workInProgressSuspendedReason);
      }
      break;
    } else {
      let renderEndTime = 0;
      if (enableProfilerTimer && enableComponentPerformanceTrack) {
        renderEndTime = now();
      }

      // 5.1 并发渲染下校验外部 store 是否一致,不一致则强制同步重渲染
      const finishedWork: Fiber = (root.current.alternate: any);
      if (
        renderWasConcurrent &&
        !isRenderConsistentWithExternalStores(finishedWork)
      ) {
        if (enableProfilerTimer && enableComponentPerformanceTrack) {
          setCurrentTrackFromLanes(lanes);
          logInconsistentRender(renderStartTime, renderEndTime);
          finalizeRender(lanes, renderEndTime);
        }
        // 外部 store 发生变更,强制同步重渲染
        exitStatus = renderRootSync(root, lanes, false);
        renderWasConcurrent = false;
        continue;
      }

      // 5.2 错误处理:如果渲染出错,尝试同步重试
      if (
        (disableLegacyMode || root.tag !== LegacyRoot) &&
        exitStatus === RootErrored
      ) {
        const lanesThatJustErrored = lanes;
        const errorRetryLanes = getLanesToRetrySynchronouslyOnError(
          root,
          lanesThatJustErrored,
        );
        if (errorRetryLanes !== NoLanes) {
          if (enableProfilerTimer && enableComponentPerformanceTrack) {
            setCurrentTrackFromLanes(lanes);
            logErroredRenderPhase(renderStartTime, renderEndTime, lanes);
            finalizeRender(lanes, renderEndTime);
          }
          lanes = errorRetryLanes;
          exitStatus = recoverFromConcurrentError(
            root,
            lanesThatJustErrored,
            errorRetryLanes,
          );
          renderWasConcurrent = false;
          // 如果重试后不再报错,则重新进入主循环
          if (exitStatus !== RootErrored) {
            continue;
          } else {
            if (enableProfilerTimer && enableComponentPerformanceTrack) {
              renderEndTime = now();
            }
          }
        }
      }

      // 5.3 致命错误,直接标记为挂起
      if (exitStatus === RootFatalErrored) {
        if (enableProfilerTimer && enableComponentPerformanceTrack) {
          setCurrentTrackFromLanes(lanes);
          logErroredRenderPhase(renderStartTime, renderEndTime, lanes);
          finalizeRender(lanes, renderEndTime);
        }
        prepareFreshStack(root, NoLanes);
        const didAttemptEntireTree = true;
        markRootSuspended(root, lanes, NoLane, didAttemptEntireTree);
        break;
      }

      // 5.4 渲染完成,进入提交阶段
      finishConcurrentRender(
        root,
        exitStatus,
        finishedWork,
        lanes,
        renderEndTime,
      );
    }
    break;
  } while (true);

  // 6. 确保根节点后续任务被调度(如有未完成的任务会继续调度)
  ensureRootIsScheduled(root);
}

这里主要关注的点是

// 4. 根据 shouldTimeSlice 选择并发或同步渲染主流程 
let exitStatus = shouldTimeSlice ? renderRootConcurrent(root, lanes) : renderRootSync(root, lanes, true);

同步模式调用renderRootSync

是 React Fiber 同步渲染的主流程函数。它的作用是在同步模式下推进 Fiber 树的渲染,确保所有工作在一次主线程任务内完成。函数首先保存当前的执行上下文,并切换到渲染上下文,然后设置当前的 Dispatcher 和异步 Dispatcher,保证 hooks 和调度行为正确。

接下来,如果当前的根节点或优先级(lanes)发生变化,会重置 Fiber 栈,准备新的渲染环境,并在开发者工具启用时处理更新追踪相关的数据结构。这一步确保每次渲染都能从正确的状态开始,避免数据错乱。

主循环部分,renderRootSync 会检查是否有挂起(如 Suspense、数据请求等)。在同步渲染下,如果遇到挂起,不会让出主线程,而是立即展开 Fiber 栈,触发降级 UI 或错误边界。如果遇到 hydration(服务端渲染的水合),会中断当前渲染,切换到 hydration 相关的 lane。对于其他挂起类型,会根据情况判断是否需要切换到预渲染模式(prerendering),如果需要则退出循环,等待并发渲染恢复。

如果没有挂起,函数会调用 workLoopSync,以同步方式推进所有 Fiber 节点的 beginWork 和 completeWork,直到整棵树渲染完成或再次遇到异常。异常会被捕获并通过 handleThrow 处理,保证渲染流程的健壮性。

渲染结束后,函数会重置上下文依赖、恢复上下文和 Dispatcher,并在启用调度分析器时记录渲染停止。最后,如果 Fiber 树还有未完成的工作,说明渲染被挂起;否则会清理相关状态,处理并发更新队列,并返回最终的退出状态。这样保证了同步渲染的高效性和一致性,同时为后续的提交(commit)阶段做好准备

并发模式调用renderRootConcurrent

源码中有几个相对比较关键的变量及概念

  1. lanes:当前正在调度的 更新优先级 Lane(Lane 模型) ,React 用 Lane 代替之前的 Priority 来管理更新优先级。
  2. fiber栈: React 通过 Fiber 节点 和 调度器 实现 Fiber 栈,它不是独立的数据结构,而是 基于 Fiber 树的一种任务调度机制,类似于树的遍历隐式维护了一个栈的效果

源码如下:

/**
 * 并发渲染根Fiber节点
 * @param root 当前Fiber根节点
 * @param lanes 当前渲染的优先级通道
 * @returns 返回渲染状态(进行中/已完成等)
 */
function renderRootConcurrent(root: FiberRoot, lanes: Lanes) {
  // 保存当前执行上下文
  const prevExecutionContext = executionContext;
  // 设置当前执行上下文为渲染模式
  executionContext |= RenderContext;

  // 切换Dispatcher,设置当前容器的context
  const prevDispatcher = pushDispatcher(root.containerInfo);
  // 切换异步Dispatcher
  const prevAsyncDispatcher = pushAsyncDispatcher();

  /**
   * 如果根节点或优先级发生变化,重置Fiber栈,否则继续上次未完成的工作
   */
  if (workInProgressRoot !== root || workInProgressRootRenderLanes !== lanes) {
    if (enableUpdaterTracking) {
      if (isDevToolsPresent) {
        const memoizedUpdaters = root.memoizedUpdaters;
        if (memoizedUpdaters.size > 0) {
          // 恢复上次未完成的updaters
          restorePendingUpdaters(root, workInProgressRootRenderLanes);
          memoizedUpdaters.clear();
        }

        // 将本次调度的Fiber从Map移动到Set,便于区分本次和未来的更新
        movePendingFibersToMemoized(root, lanes);
      }
    }

    // 获取本次调度相关的transition
    workInProgressTransitions = getTransitionsForLanes(root, lanes);

    // 重置渲染计时器
    resetRenderTimer();
    // 构建新的Fiber栈
    prepareFreshStack(root, lanes);
  } else {
    // 继续上次未完成的工作
    // 如果之前处于预渲染模式,检查是否有新数据
    workInProgressRootIsPrerendering = checkIfRootIsPrerendering(root, lanes);
  }

  // 调度分析工具标记渲染开始
  if (enableSchedulingProfiler) {
    markRenderStarted(lanes);
  }

  /**
   * 主循环,处理Fiber树
   * 使用标签循环以便在挂起时可以跳出多层循环
   */
  outer: do {
    try {
      /**
       * 如果当前work loop处于挂起状态,处理挂起逻辑
       */
      if (
        workInProgressSuspendedReason !== NotSuspended &&
        workInProgress !== null
      ) {
        // 当前单元Fiber
        const unitOfWork = workInProgress;
        // 挂起时抛出的值(如Promise)
        const thrownValue = workInProgressThrownValue;

        // 根据挂起原因处理
        resumeOrUnwind: switch (workInProgressSuspendedReason) {
          case SuspendedOnError: {
            // 发生错误,重置挂起状态,展开栈
            workInProgressSuspendedReason = NotSuspended;
            workInProgressThrownValue = null;
            throwAndUnwindWorkLoop(
              root,
              unitOfWork,
              thrownValue,
              SuspendedOnError,
            );
            break;
          }
          case SuspendedOnData:
          case SuspendedOnAction: {
            // 挂起在数据Promise上
            const thenable: Thenable<mixed> = (thrownValue: any);
            if (isThenableResolved(thenable)) {
              // 数据已就绪,重试渲染
              workInProgressSuspendedReason = NotSuspended;
              workInProgressThrownValue = null;
              replaySuspendedUnitOfWork(unitOfWork);
              break;
            }
            // 数据未就绪,注册回调,等待数据
            const onResolution = () => {
              if (
                (workInProgressSuspendedReason === SuspendedOnData ||
                  workInProgressSuspendedReason === SuspendedOnAction) &&
                workInProgressRoot === root
              ) {
                workInProgressSuspendedReason = SuspendedAndReadyToContinue;
              }
              ensureRootIsScheduled(root);
            };
            thenable.then(onResolution, onResolution);
            break outer; // 跳出外层循环
          }
          case SuspendedOnImmediate: {
            // 立即挂起,切换为"准备继续"状态,等待主线程机会
            workInProgressSuspendedReason = SuspendedAndReadyToContinue;
            break outer; // 跳出外层循环
          }
          case SuspendedOnInstance: {
            // 资源实例挂起,切换为"实例准备继续"状态
            workInProgressSuspendedReason =
              SuspendedOnInstanceAndReadyToContinue;
            break outer; // 跳出外层循环
          }
          case SuspendedAndReadyToContinue: {
            // 数据已准备好,重试渲染,否则展开栈
            const thenable: Thenable<mixed> = (thrownValue: any);
            if (isThenableResolved(thenable)) {
              workInProgressSuspendedReason = NotSuspended;
              workInProgressThrownValue = null;
              replaySuspendedUnitOfWork(unitOfWork);
            } else {
              workInProgressSuspendedReason = NotSuspended;
              workInProgressThrownValue = null;
              throwAndUnwindWorkLoop(
                root,
                unitOfWork,
                thrownValue,
                SuspendedAndReadyToContinue,
              );
            }
            break;
          }
          case SuspendedOnInstanceAndReadyToContinue: {
            // 检查资源是否已准备好
            let resource: null | Resource = null;
            switch (workInProgress.tag) {
              case HostHoistable: {
                resource = workInProgress.memoizedState;
              }
              // intentional fallthrough
              case HostComponent:
              case HostSingleton: {
                const hostFiber = workInProgress;
                const type = hostFiber.type;
                const props = hostFiber.pendingProps;
                const isReady = resource
                  ? preloadResource(resource)
                  : preloadInstance(hostFiber.stateNode, type, props);
                if (isReady) {
                  // 资源已准备好,继续work loop
                  workInProgressSuspendedReason = NotSuspended;
                  workInProgressThrownValue = null;
                  const sibling = hostFiber.sibling;
                  if (sibling !== null) {
                    workInProgress = sibling;
                  } else {
                    const returnFiber = hostFiber.return;
                    if (returnFiber !== null) {
                      workInProgress = returnFiber;
                      completeUnitOfWork(returnFiber);
                    } else {
                      workInProgress = null;
                    }
                  }
                  break resumeOrUnwind; // 跳出switch
                }
                break;
              }
              default: {
                // 非预期类型,开发环境下报错
                if (__DEV__) {
                  console.error(
                    'Unexpected type of fiber triggered a suspensey commit. ' +
                      'This is a bug in React.',
                  );
                }
                break;
              }
            }
            // 资源未准备好,展开栈
            workInProgressSuspendedReason = NotSuspended;
            workInProgressThrownValue = null;
            throwAndUnwindWorkLoop(
              root,
              unitOfWork,
              thrownValue,
              SuspendedOnInstanceAndReadyToContinue,
            );
            break;
          }
          case SuspendedOnDeprecatedThrowPromise: {
            // 兼容旧的throw promise模式,直接展开栈
            workInProgressSuspendedReason = NotSuspended;
            workInProgressThrownValue = null;
            throwAndUnwindWorkLoop(
              root,
              unitOfWork,
              thrownValue,
              SuspendedOnDeprecatedThrowPromise,
            );
            break;
          }
          case SuspendedOnHydration: {
            // 水合挂起,重置Fiber栈,退出循环
            resetWorkInProgressStack();
            workInProgressRootExitStatus = RootSuspendedAtTheShell;
            break outer; // 跳出外层循环
          }
          default: {
            throw new Error(
              'Unexpected SuspendedReason. This is a bug in React.',
            );
          }
        }
      }

      /**
       * 如果在act测试环境下,不判断shouldYield,直接同步work loop
       */
      if (__DEV__ && ReactSharedInternals.actQueue !== null) {
        workLoopSync();
      } else if (enableThrottledScheduling) {
        // 启用节流调度,分帧work loop
        workLoopConcurrent(includesNonIdleWork(lanes));
      } else {
        // 默认并发work loop,遇到shouldYield时让出主线程
        workLoopConcurrentByScheduler();
      }
      break; // 正常完成work loop,跳出switch
    } catch (thrownValue) {
      // 捕获异常,处理错误或挂起
      handleThrow(root, thrownValue);
    }
  } while (true); // 持续循环直到完成或挂起

  // 重置context依赖
  resetContextDependencies();

  // 恢复Dispatcher和执行上下文
  popDispatcher(prevDispatcher);
  popAsyncDispatcher(prevAsyncDispatcher);
  executionContext = prevExecutionContext;

  /**
   * 判断Fiber树是否完成
   */
  if (workInProgress !== null) {
    // 还有未完成的工作,返回进行中状态
    if (enableSchedulingProfiler) {
      markRenderYielded();
    }
    return RootInProgress;
  } else {
    // 树已完成,清理全局状态
    if (enableSchedulingProfiler) {
      markRenderStopped();
    }
    workInProgressRoot = null;
    workInProgressRootRenderLanes = NoLanes;
    // 渲染阶段完成,可以安全处理并发更新队列
    finishQueueingConcurrentUpdates();
    // 返回最终退出状态
    return workInProgressRootExitStatus;
  }
}
            

是 React Fiber 并发渲染的核心函数。它负责以并发模式(即可中断、可恢复的方式)对 Fiber 树进行渲染。函数首先保存当前的执行上下文,并设置为渲染上下文,然后初始化调度器和异步调度器。

接下来,函数会判断当前的 root 或 lanes(优先级)是否发生变化。如果发生变化,则重置 Fiber 栈,准备一次全新的渲染;否则,继续上一次未完成的渲染任务(对挂起的任务做了细致的恢复处理)。此处还涉及开发者工具相关的 updater 跟踪和事务管理,确保调试信息的准确性。

在主循环中,函数会根据当前的挂起(suspended)状态,决定是回放(replay)被挂起的单元,还是展开(unwind)Fiber 栈。对于不同的挂起原因(如数据请求、错误、实例未就绪等),采取不同的恢复或中断策略。例如,如果数据已就绪,会尝试重新渲染;如果还未就绪,则挂起等待,并通过 thenable 的回调确保后续能恢复渲染。

渲染主循环采用 do-while 结构,支持异常捕获和恢复。渲染过程中会根据环境选择同步或并发的工作循环(workLoopSync、workLoopConcurrent、workLoopConcurrentByScheduler)。渲染结束后,重置上下文和调度器,并根据是否还有未完成的工作返回不同的状态。

在这里主要关注的函数是workLoopConcurrentByScheduler,它是构建的默认调度

workLoopConcurrentByScheduler
function workLoopConcurrentByScheduler() {  
// Perform work until Scheduler asks us to yield  
while (workInProgress !== null && !shouldYield()) {  
// $FlowFixMe[incompatible-call] flow doesn't know that shouldYield() is side-effect free  
performUnitOfWork(workInProgress);  
}  
}

每处理一部分 Fiber 节点后,通过shouldYield()判断是否需要让出主线程。如果需要,就暂停渲染,等待浏览器空闲时再继续。
这种方式可以让 React 渲染过程与浏览器的高优先级任务(如输入、动画)协同,不会长时间阻塞主线程。 进一步的,下面进入performUnitOfWork

performUnitOfWork
function performUnitOfWork(unitOfWork: Fiber): void {
  // 当前已刷新的Fiber状态存储在alternate属性中
  // 理想情况下不应该依赖这个属性,但在这里使用它可以避免在工作进行中需要额外的字段
  const current = unitOfWork.alternate;

  let next; // 下一个要处理的工作单元

  // 如果启用了性能分析计时器且当前Fiber处于性能分析模式
  if (enableProfilerTimer && (unitOfWork.mode & ProfileMode) !== NoMode) {
    // 开始记录当前Fiber的性能分析计时器
    startProfilerTimer(unitOfWork);
    
    // 在开发环境下使用runWithFiberInDEV包装beginWork调用
    // 这可能是为了在开发模式下提供额外的调试信息或错误检查
    if (__DEV__) {
      next = runWithFiberInDEV(
        unitOfWork,
        beginWork,       // 开始处理当前Fiber的工作
        current,         // 当前已刷新的Fiber
        unitOfWork,      // 正在处理的工作单元
        entangledRenderLanes, // 相关的渲染通道
      );
    } else {
      // 生产环境下直接调用beginWork
      next = beginWork(current, unitOfWork, entangledRenderLanes);
    }
    
    // 停止性能分析计时器并记录持续时间
    stopProfilerTimerIfRunningAndRecordDuration(unitOfWork);
  } else {
    // 如果没有启用性能分析计时器
    if (__DEV__) {
      // 开发环境下同样使用runWithFiberInDEV包装beginWork调用
      next = runWithFiberInDEV(
        unitOfWork,
        beginWork,
        current,
        unitOfWork,
        entangledRenderLanes,
      );
    } else {
      // 生产环境下直接调用beginWork
      next = beginWork(current, unitOfWork, entangledRenderLanes);
    }
  }

  // 将当前Fiber的memoizedProps更新为pendingProps
  // 这表示当前Fiber的props已经被处理并可以用于后续渲染
  unitOfWork.memoizedProps = unitOfWork.pendingProps;

  // 如果next为null,表示当前Fiber没有产生新的工作单元
  if (next === null) {
    // 完成当前Fiber的工作
    completeUnitOfWork(unitOfWork);
  } else {
    // 否则,将workInProgress设置为下一个要处理的工作单元
    // 这将使得后续的处理继续在这个新的Fiber上进行
    workInProgress = next;
  }
}

performUnitOfWork函数,用于执行单个Fiber单元的工作并决定下一步要处理的Fiber

  • 参数unitOfWork:当前要处理的Fiber节点

  • 返回void:通过修改全局变量workInProgress来驱动工作循环

  • 通过beginWorkcompleteUnitOfWork的配合实现深度优先遍历

  • 这个函数是Fiber架构工作循环的核心,实现了"处理当前节点->获取下一个节点"的流程

  • 处理当前节点的收尾工作,比如创建 DOM 节点、收集副作用(如插入、更新、删除等)。

这里重点要注意的是:

  • 递归地处理兄弟节点(sibling) ,即横向遍历。
  • 当没有兄弟节点时,回溯到父节点(return) ,即向上返回,继续完成父节点的收尾工作。

completeUnitOfWork 是“离开”一个节点的地方,它负责完成当前节点并转向兄弟节点或回溯到父节点

下面看看beginWork是如何对每一个节点做处理的

beginWork
beginWork  
function beginWork(  
current: Fiber | null,  
workInProgress: Fiber,  
renderLanes: Lanes,  
): Fiber | null {  
// 开发环境下,如果需要重新挂载(如热更新),则创建新 fiber 并 remount  
if (__DEV__) {  
if (workInProgress._debugNeedsRemount && current !== null) {  
// 重新挂载 fiber,通常用于调试或热重载  
const copiedFiber = createFiberFromTypeAndProps(  
workInProgress.type,  
workInProgress.key,  
workInProgress.pendingProps,  
workInProgress._debugOwner || null,  
workInProgress.mode,  
workInProgress.lanes,  
);  
copiedFiber._debugStack = workInProgress._debugStack;  
copiedFiber._debugTask = workInProgress._debugTask;  
return remountFiber(current, workInProgress, copiedFiber);  
}  
}  
  
// 判断是否是更新流程  
if (current !== null) {  
const oldProps = current.memoizedProps;  
const newProps = workInProgress.pendingProps;  
  
// 如果 props、context 发生变化,或类型变了(如热更新),则标记需要更新  
if (  
oldProps !== newProps ||  
hasLegacyContextChanged() ||  
(__DEV__ ? workInProgress.type !== current.type : false)  
) {  
didReceiveUpdate = true;  
} else {  
// 否则检查是否有调度的更新或 context 变化  
const hasScheduledUpdateOrContext = checkScheduledUpdateOrContext(  
current,  
renderLanes,  
);  
if (  
!hasScheduledUpdateOrContext &&  
// 错误边界或 suspense 的二次渲染也要特殊处理  
(workInProgress.flags & DidCapture) === NoFlags  
) {  
// 没有更新,直接 bailout,跳过本节点  
didReceiveUpdate = false;  
return attemptEarlyBailoutIfNoScheduledUpdate(  
current,  
workInProgress,  
renderLanes,  
);  
}  
// 兼容 legacy suspense 的特殊处理  
if ((current.flags & ForceUpdateForLegacySuspense) !== NoFlags) {  
didReceiveUpdate = true;  
} else {  
didReceiveUpdate = false;  
}  
}  
} else {  
// 首次挂载流程  
didReceiveUpdate = false;  
  
// SSR hydration 场景下,处理多分支子节点的 id 生成  
if (getIsHydrating() && isForkedChild(workInProgress)) {  
const slotIndex = workInProgress.index;  
const numberOfForks = getForksAtLevel(workInProgress);  
pushTreeId(workInProgress, numberOfForks, slotIndex);  
}  
}  
  
// 进入 begin 阶段前,清空本 fiber 的 lanes(优先级)  
workInProgress.lanes = NoLanes;  
  
// 根据 fiber 的 tag 类型,分发到不同的处理函数  
switch (workInProgress.tag) {  
case LazyComponent: {  
// 懒加载组件  
const elementType = workInProgress.elementType;  
return mountLazyComponent(  
current,  
workInProgress,  
elementType,  
renderLanes,  
);  
}  
case FunctionComponent: {  
// 函数组件  
const Component = workInProgress.type;  
const unresolvedProps = workInProgress.pendingProps;  
const resolvedProps =  
disableDefaultPropsExceptForClasses ||  
workInProgress.elementType === Component  
? unresolvedProps  
: resolveDefaultPropsOnNonClassComponent(Component, unresolvedProps);  
return updateFunctionComponent(  
current,  
workInProgress,  
Component,  
resolvedProps,  
renderLanes,  
);  
}  
case ClassComponent: {  
// 类组件  
const Component = workInProgress.type;  
const unresolvedProps = workInProgress.pendingProps;  
const resolvedProps = resolveClassComponentProps(  
Component,  
unresolvedProps,  
workInProgress.elementType === Component,  
);  
return updateClassComponent(  
current,  
workInProgress,  
Component,  
resolvedProps,  
renderLanes,  
);  
}  
case HostRoot:  
// 根节点  
return updateHostRoot(current, workInProgress, renderLanes);  
case HostHoistable:  
// 可提升的宿主节点  
if (supportsResources) {  
return updateHostHoistable(current, workInProgress, renderLanes);  
}  
// fall through  
case HostSingleton:  
// 单例宿主节点  
if (supportsSingletons) {  
return updateHostSingleton(current, workInProgress, renderLanes);  
}  
// fall through  
case HostComponent:  
// 普通宿主节点(如 div、span)  
return updateHostComponent(current, workInProgress, renderLanes);  
case HostText:  
// 文本节点  
return updateHostText(current, workInProgress);  
case SuspenseComponent:  
// Suspense 边界  
return updateSuspenseComponent(current, workInProgress, renderLanes);  
case HostPortal:  
// Portal 节点  
return updatePortalComponent(current, workInProgress, renderLanes);  
case ForwardRef: {  
// forwardRef 组件  
const type = workInProgress.type;  
const unresolvedProps = workInProgress.pendingProps;  
const resolvedProps =  
disableDefaultPropsExceptForClasses ||  
workInProgress.elementType === type  
? unresolvedProps  
: resolveDefaultPropsOnNonClassComponent(type, unresolvedProps);  
return updateForwardRef(  
current,  
workInProgress,  
type,  
resolvedProps,  
renderLanes,  
);  
}  
case Fragment:  
// Fragment 片段  
return updateFragment(current, workInProgress, renderLanes);  
case Mode:  
// Mode 组件  
return updateMode(current, workInProgress, renderLanes);  
case Profiler:  
// Profiler 组件  
return updateProfiler(current, workInProgress, renderLanes);  
case ContextProvider:  
// Context Provider  
return updateContextProvider(current, workInProgress, renderLanes);  
case ContextConsumer:  
// Context Consumer  
return updateContextConsumer(current, workInProgress, renderLanes);  
case MemoComponent: {  
// React.memo 组件  
const type = workInProgress.type;  
const unresolvedProps = workInProgress.pendingProps;  
// 先解析外层 defaultProps,再解析内层  
let resolvedProps = disableDefaultPropsExceptForClasses  
? unresolvedProps  
: resolveDefaultPropsOnNonClassComponent(type, unresolvedProps);  
resolvedProps = disableDefaultPropsExceptForClasses  
? resolvedProps  
: resolveDefaultPropsOnNonClassComponent(type.type, resolvedProps);  
return updateMemoComponent(  
current,  
workInProgress,  
type,  
resolvedProps,  
renderLanes,  
);  
}  
case SimpleMemoComponent: {  
// 简单 memo 组件  
return updateSimpleMemoComponent(  
current,  
workInProgress,  
workInProgress.type,  
workInProgress.pendingProps,  
renderLanes,  
);  
}  
case IncompleteClassComponent: {  
// 挂起的类组件(legacy 模式)  
if (disableLegacyMode) {  
break;  
}  
const Component = workInProgress.type;  
const unresolvedProps = workInProgress.pendingProps;  
const resolvedProps = resolveClassComponentProps(  
Component,  
unresolvedProps,  
workInProgress.elementType === Component,  
);  
return mountIncompleteClassComponent(  
current,  
workInProgress,  
Component,  
resolvedProps,  
renderLanes,  
);  
}  
case IncompleteFunctionComponent: {  
// 挂起的函数组件(legacy 模式)  
if (disableLegacyMode) {  
break;  
}  
const Component = workInProgress.type;  
const unresolvedProps = workInProgress.pendingProps;  
const resolvedProps = resolveClassComponentProps(  
Component,  
unresolvedProps,  
workInProgress.elementType === Component,  
);  
return mountIncompleteFunctionComponent(  
current,  
workInProgress,  
Component,  
resolvedProps,  
renderLanes,  
);  
}  
case SuspenseListComponent: {  
// SuspenseList 组件  
return updateSuspenseListComponent(current, workInProgress, renderLanes);  
}  
case ScopeComponent: {  
// Scope 组件  
if (enableScopeAPI) {  
return updateScopeComponent(current, workInProgress, renderLanes);  
}  
break;  
}  
case ActivityComponent: {  
// Activity 组件  
return updateActivityComponent(current, workInProgress, renderLanes);  
}  
case OffscreenComponent: {  
// Offscreen 组件  
return updateOffscreenComponent(  
current,  
workInProgress,  
renderLanes,  
workInProgress.pendingProps,  
);  
}  
case LegacyHiddenComponent: {  
// 隐藏组件(legacy 模式)  
if (enableLegacyHidden) {  
return updateLegacyHiddenComponent(  
current,  
workInProgress,  
renderLanes,  
);  
}  
break;  
}  
case CacheComponent: {  
// Cache 组件  
return updateCacheComponent(current, workInProgress, renderLanes);  
}  
case TracingMarkerComponent: {  
// TracingMarker 组件  
if (enableTransitionTracing) {  
return updateTracingMarkerComponent(  
current,  
workInProgress,  
renderLanes,  
);  
}  
break;  
}  
case ViewTransitionComponent: {  
// ViewTransition 组件  
if (enableViewTransition) {  
return updateViewTransition(current, workInProgress, renderLanes);  
}  
break;  
}  
case Throw: {  
// 调和阶段抛出的异常,直接抛出  
throw workInProgress.pendingProps;  
}  
}  
  
// 未知 tag,抛出错误  
throw new Error(  
`Unknown unit of work tag (${workInProgress.tag}). This error is likely caused by a bug in ` +  
'React. Please file an issue.',  
);  
}

这里引用一张kasong的图

来源网址: react.iamkasong.com/process/beg…

Clipboard_Screenshot_1752564520.png

completeUnitOfWork
function completeUnitOfWork(unitOfWork: Fiber): void {
  // Attempt to complete the current unit of work, then move to the next
  // sibling. If there are no more siblings, return to the parent fiber.
  let completedWork: Fiber = unitOfWork;
  do {
    if ((completedWork.flags & Incomplete) !== NoFlags) {
      // This fiber did not complete, because one of its children did not
      // complete. Switch to unwinding the stack instead of completing it.
      //
      // The reason "unwind" and "complete" is interleaved is because when
      // something suspends, we continue rendering the siblings even though
      // they will be replaced by a fallback.
      const skipSiblings = workInProgressRootDidSkipSuspendedSiblings;
      unwindUnitOfWork(completedWork, skipSiblings);
      return;
    }

    // The current, flushed, state of this fiber is the alternate. Ideally
    // nothing should rely on this, but relying on it here means that we don't
    // need an additional field on the work in progress.
    const current = completedWork.alternate;
    const returnFiber = completedWork.return;

    let next;
    startProfilerTimer(completedWork);
    if (__DEV__) {
      next = runWithFiberInDEV(
        completedWork,
        completeWork,
        current,
        completedWork,
        entangledRenderLanes,
      );
    } else {
      next = completeWork(current, completedWork, entangledRenderLanes);
    }
    if (enableProfilerTimer && (completedWork.mode & ProfileMode) !== NoMode) {
      // Update render duration assuming we didn't error.
      stopProfilerTimerIfRunningAndRecordIncompleteDuration(completedWork);
    }
    if (next !== null) {
      // Completing this fiber spawned new work. Work on that next.
      workInProgress = next;
      return;
    }

    const siblingFiber = completedWork.sibling;
    if (siblingFiber !== null) {
      // If there is more work to do in this returnFiber, do that next.
      workInProgress = siblingFiber;
      return;
    }
    // Otherwise, return to the parent
    // $FlowFixMe[incompatible-type] we bail out when we get a null
    completedWork = returnFiber;
    // Update the next thing we're working on in case something throws.
    workInProgress = completedWork;
  } while (completedWork !== null);

  // We've reached the root.
  if (workInProgressRootExitStatus === RootInProgress) {
    workInProgressRootExitStatus = RootCompleted;
  }
}

completeUnitOfWork 函数的主要职责是完成当前的工作单元(unitOfWork),即一个 Fiber 节点,然后根据情况移动到下一个兄弟节点或返回到父节点,直到完成整个工作循环。如果在完成过程中发现某个子节点未完成,函数会切换到展开(unwind)操作,而不是继续完成当前节点。

关键点解析

1. 检查是否完成:

  • 使用 completedWork.flags & Incomplete 检查当前 Fiber 是否标记为未完成。如果是,调用 unwindUnitOfWork 进行展开操作,并结束当前函数。

2. 获取相关 Fiber:

  • current: 当前 Fiber 的备选(alternate)状态,用于在开发模式下进行比较或恢复。

  • returnFiber: 当前 Fiber 的父节点,用于在完成当前节点后返回到父节点继续处理。

3. 执行 completeWork :

  • 根据是否在开发模式 (__DEV__),调用 completeWork 函数完成当前 Fiber 的工作。

  • 在开发模式下,runWithFiberInDEV 用于包装 completeWork,可能用于调试或性能分析。

  • 在生产模式下,直接调用 completeWork

4. 性能分析:

  • 使用 startProfilerTimer 和 stopProfilerTimerIfRunningAndRecordIncompleteDuration 进行性能计时,记录渲染时间,尤其是在启用了性能分析 (enableProfilerTimer) 且当前 Fiber 处于 ProfileMode 时。

5. 处理新生成的工作:

  • 如果 completeWork 返回了一个新的 next 工作单元,将 workInProgress 设置为 next,并结束当前函数,继续处理新的工作。

6. 处理兄弟节点和父节点:

  • 如果存在兄弟节点 (siblingFiber),将 workInProgress 设置为兄弟节点,继续处理兄弟节点。

  • 如果没有兄弟节点,将 completedWork 设置为其父节点 (returnFiber),并将 workInProgress 更新为父节点,继续向上处理。

7. 完成根节点:

  • 当循环结束时,表示已经处理到根节点。如果根的工作进度状态是 RootInProgress,将其设置为 RootCompleted,表示整个工作循环已完成。

总结

completeUnitOfWork 是 React 渲染过程中负责完成单个 Fiber 节点工作的重要函数。它确保每个节点在其子节点完成之后被正确处理,并根据需要移动到兄弟节点或父节点,以完成整个渲染树的遍历。这一过程对于 React 的协调(Reconciliation)算法至关重要,确保高效的更新和渲染。