Skip to content

协调阶段的开始

协调阶段的开始是 React 整个更新流程中至关重要的一步,它标志着 React 开始计算和准备对 UI 进行实际更改。这个阶段的启动是由调度阶段(Scheduling Phase)完成的,具体来说,是由调度器(Scheduler)在浏览器空闲时回调执行的。

在调度阶段,当一个更新被触发后,最终会通过 ensureRootIsScheduled 函数,将一个回调函数(通常是 performSyncWorkOnRootperformWorkOnRootViaSchedulerTask )提交给 Scheduler(调度器)。Scheduler 会在浏览器主线程空闲时执行这个回调,从而正式开启协调阶段,这两个函数是协调阶段的真正入口。

  • performSyncWorkOnRoot(root) : 当有同步更新(如高优先级事件回调、 ReactDOM.render 的首次渲染)时,调度器会安排这个函数。它会同步地执行协调工作,不中断地完成整个 Fiber 树的遍历。
  • performWorkOnRootViaSchedulerTask(root, didTimeout) : 当有并发更新(如 setState 、 useState 、 startTransition 等)时,调度器会安排这个函数。它会以可中断的方式执行协调工作,允许 React 在必要时暂停并交出控制权给浏览器,以响应用户输入或执行其他高优先级任务。

performSyncWorkOnRoot

performSyncWorkOnRoot 伪代码
javascript
function performSyncWorkOnRoot(root: FiberRoot, lanes: Lanes) {
  // 1. 准备工作 (Preparation)
  const originalIs  = isWorking;
  isWorking = true;

  // 2. 执行同步渲染 (Perform Synchronous Render)
  // 调用 renderRootSync 函数,它会同步地遍历 Fiber 树,执行组件的 render 方法,
  // 比较新旧 Fiber 节点,并构建 workInProgress 树。这个过程是不可中断的。
  let exitStatus = renderRootSync(root, lanes);

  // 3. 处理结果 (Handle Result)
  // 根据 renderRootSync 的执行结果(exitStatus)来决定后续操作。
  if (exitStatus === RootDidComplete) {
    // 如果协调工作成功完成,则准备进入提交阶段。
    const finishedWork = root.current.alternate;
    root.finishedWork = finishedWork;
    root.finishedLanes = lanes;
    // 调用 finishSyncRender 来完成同步渲染,并触发提交阶段。
    finishSyncRender(root, exitStatus, lanes);
  } else if (exitStatus === RootFatalError) {
    // 如果在协调过程中发生致命错误,则重置根节点的状态,并抛出错误。
    // 这通常意味着 Fiber 树在处理过程中遇到了无法恢复的问题。
    root.finishedWork = null;
    root.finishedLanes = NoLanes;
    // 清理可能存在的挂起任务或回调。
    cancelCallback(root.callbackNode);
    root.callbackNode = null;
    root.callbackPriority = NoLane;
    // 抛出错误,以便上层捕获和处理。
    throw new Error('A fatal error occurred during synchronous rendering.');
  } else {
    // 对于同步工作,通常不会出现 RootDidNotComplete 或 RootSuspended 等状态,
  }
  // 恢复之前的工作状态。
  isWorking = originalIsWorking;
}

详解上面的代码,我们就能够发现其内部完整流程。

  1. 准备工作 :

    • isWorking = true; :这是一个内部标志,用于指示 React 当前正在执行工作。在同步工作期间,这个标志通常会设置为 true ,以防止其他并发任务干扰。
  2. 执行同步渲染 ( renderRootSync ) :

    • renderRootSync(root, lanes)performSyncWorkOnRoot 的核心。它负责执行实际的协调过程。对于同步更新, renderRootSync 会在一个不可中断的循环中遍历整个 Fiber 树,从根节点开始,向下递归地处理每个 Fiber 节点。
    • 在这个过程中,React 会执行组件的 render 方法,计算新的 props 和 state,并应用 Diff 算法来比较新旧 Fiber 节点。所有需要进行的 DOM 操作(如插入、更新、删除)都会被标记在 Fiber 节点上,形成一个副作用列表。
    • 由于是同步工作, renderRootSync 会尝试一次性完成所有协调工作,不会进行时间切片或中断。
  3. 处理结果 :

    • exitStatus : renderRootSync 函数的返回值,表示协调工作的最终状态。
    • RootDidComplete :如果 exitStatus 等于 RootDidComplete ,表示整个协调过程成功完成,并且 workInProgress 树已经构建完毕。此时, root.finishedWork 会指向这个新的 workInProgress 树, root.finishedLanes 记录了本次处理的优先级。
      • finishSyncRender(root, exitStatus, lanes) :这个函数会负责触发提交阶段(Commit Phase)。在commit phase章节会进行详细的讲解
    • RootFatalError :如果 exitStatus 等于 RootFatalError ,表示在协调过程中发生了无法恢复的错误。React 会清理根节点的状态(如取消挂起的调度任务),并重新抛出这个错误,以便应用程序可以捕获并处理它。这通常发生在组件渲染过程中抛出未捕获的错误,导致 Fiber 树处于不一致状态时。
    • 其他状态:对于同步工作,理论上不会出现 RootDidNotComplete (未完成)或 RootSuspended (挂起)等状态,因为同步工作会一直执行直到完成或出错。如果出现,则表明存在异常情况,需要更复杂的错误处理逻辑。

performWorkOnRootViaSchedulerTask

performWorkOnRootViaSchedulerTask是 React 调度器回调执行的入口,它负责在浏览器空闲时执行协调工作,其核心逻辑将更加清晰,主要聚焦于协调阶段的实际工作,他的主要工作包括

  • 处理挂起的副作用 :首先刷新所有挂起的被动副作用(如 useEffect 的回调)。
  • 获取工作优先级 :根据根节点当前的更新状态,确定本次协调需要处理的优先级。
  • 执行协调工作 :进入工作循环,执行 Fiber 树的协调(render)工作,构建 workInProgress 树。
  • 提交或中断 :根据协调结果,决定是提交渲染结果到 DOM,还是中断工作并重新调度。
performSyncWorkOnRoot 伪代码
javascript
function performWorkOnRootViaSchedulerTask(
  root: FiberRoot,
  didTimeout: boolean,
): RenderTaskFn | null {
  // ... existing code ...

  // 1. 处理挂起的副作用 (Flush pending passive effects)
  const originalCallbackNode = root.callbackNode;
  const didFlushPassiveEffects = flushPendingEffects(true);
  if (didFlushPassiveEffects) {
    // 如果副作用刷新导致当前任务被取消(例如,副作用中触发了新的高优先级更新),
    // 则退出当前任务,等待新的调度。
    if (root.callbackNode !== originalCallbackNode) {
      root.callbackNode = null;
      root.callbackPriority = NoLane;
      return null;
    }
  }

  // 2. 获取工作优先级 (Get work lanes)
  let lanes = getLanesToSync(root);
  if (lanes === NoLanes) {
    // 如果没有需要同步处理的更新,则尝试获取下一个需要并发处理的更新。
    lanes = getNextLanes(root, NoLanes, false);
    if (lanes === NoLanes) {
      // 如果仍然没有更新,则表示当前任务已完成,可以退出。
      root.callbackNode = null;
      root.callbackPriority = NoLane;
      return null;
    }
  }

  // 3. 执行协调工作 (Perform reconciliation work)
  let exitStatus = renderRootSync(root, lanes);

  // 4. 提交或中断 (Commit or yield)
  if (exitStatus === RootFatalError) {
    // 发生致命错误,重置状态并退出。
    root.callbackNode = null;
    root.callbackPriority = NoLane;
    return null;
  }

  if (exitStatus === RootDidComplete) {
    // 协调工作完成,准备进入提交阶段。
    // 在提交之前,可能还需要处理一些收尾工作,例如调度被动效果。
    const finishedWork = root.current.alternate;
    root.finishedWork = finishedWork;
    root.finishedLanes = lanes;
    finishConcurrentRender(root, exitStatus, lanes);
    return null;
  }

  // 如果协调工作被中断(例如,时间切片用尽),则返回一个函数,
  return performWorkOnRootViaSchedulerTask.bind(null, root);
}

详解上面的代码,我们就能够发现其内部完整流程。

  1. 处理挂起的副作用 ( flushPendingEffects ) :

    • 在每次协调工作开始前,React 会确保所有挂起的被动副作用(如 useEffect 、 useLayoutEffect 的回调)都已执行。这是为了保证副作用的执行顺序和时机,避免在新的渲染周期开始前,旧的副作用还未清理或执行完毕
    • 如果 flushPendingEffects 导致 root.callbackNode 发生变化(意味着有新的、更高优先级的任务被调度),则当前 performWorkOnRootViaSchedulerTask 任务会立即退出,将控制权交还给调度器,以便处理新的高优先级任务。
  2. 获取工作优先级 :

    • React 使用 Lanes 模型来管理更新的优先级。 getLanesToSync 会尝试获取需要同步处理的最高优先级更新。
    • 如果不存在同步更新, getNextLanes 会获取下一个需要并发处理的更新。如果仍然没有更新,说明当前根节点没有待处理的任务,函数直接返回 null ,表示任务完成。
  3. 执行协调工作 ( renderRootSync ) :

    • 这是协调阶段的核心。 renderRootSync 函数(或其并发版本 renderRootConcurrent )会启动一个工作循环,遍历 Fiber 树。
    • 在这个过程中,React 会比较当前 Fiber 树( current 树)和新的元素(JSX),生成新的 workInProgress 树。它会执行组件的 render 方法,计算新的 props 和 state,并进行 Diff 算法,标记需要更新的节点及其副作用(如插入、更新、删除等)。
    • 这个过程是可中断的,如果时间切片用尽或者有更高优先级的任务介入, renderRootSync 可能会暂停并返回一个状态,等待调度器再次调用。
  4. 提交或中断 ( finishConcurrentRender ) :

    • exitStatus 表示 renderRootSync 的执行结果。
    • 如果 exitStatusRootFatalError ,表示在协调过程中发生了不可恢复的错误,当前任务会被终止。
    • 如果 exitStatusRootDidComplete ,表示协调工作成功完成。此时, finishedWork (即构建完成的 workInProgress 树)会被赋值给 root.finishedWork ,并调用 finishConcurrentRender 来触发提交阶段(Commit Phase),将 workInProgress 树的更改应用到实际的 DOM 上。
    • 如果 exitStatus 表示协调工作被中断(例如,时间切片用尽), performWorkOnRootViaSchedulerTask 会返回自身,以便调度器在下次空闲时继续执行未完成的工作。这体现了 React 并发模式的可中断性。

在上面的函数中,其内部会调用 renderRootSyncrenderRootConcurrent 函数来执行实际的渲染工作。这两个函数都有各自的入口点,但它们的主要区别在于:

  • renderRootSync: 这个函数会立即执行所有的同步渲染工作,直到所有的更新都被处理完毕。它会在当前的 JavaScript 执行栈上执行,不会创建新的栈帧。
  • renderRootConcurrent: 这个函数会在浏览器的空闲时间执行渲染工作。它会创建一个新的栈帧,在浏览器空闲时执行。

renderRootSync

renderRootSync 伪代码
javascript
// 主要职责:以同步、不可中断的方式执行 Fiber 树的协调和渲染工作。
function renderRootSync(root: FiberRoot, lanes: Lanes): RootExitStatus {
  // 1. 记录原始的执行上下文,并设置当前为渲染上下文
  const prevExecutionContext = executionContext;
  executionContext |= RenderContext;

  // 2. 记录原始的调度优先级 (如果适用)
  //    在同步模式下,通常会提升到最高优先级执行
  const prevDispatcher = ReactCurrentDispatcher.current;
  ReactCurrentDispatcher.current = ContextOnlyDispatcher;

  // 3. 初始化或重置渲染相关的全局/模块级变量
  //    workInProgressRoot: 当前正在处理的 Root Fiber 的副本 (work-in-progress tree 的根)
  //    workInProgressRootRenderLanes: 当前渲染过程需要处理的 lanes
  //    workInProgress: 当前正在处理的 Fiber 节点
  //    ...其他状态变量
  if (workInProgressRoot !== root || workInProgressRootRenderLanes !== lanes) {
    // 如果是新的渲染任务,或者 lanes 发生变化,需要准备新的栈帧
    prepareFreshStack(root, lanes);
    // workInProgressRoot = root;
    // workInProgressRootRenderLanes = lanes;
    // workInProgress = createWorkInProgress(root.current, null);
  }

  // 4. Profiler 相关逻辑 (如果开启)
  //    - 标记渲染开始
  if (__DEV__ && enableProfilerTimer) {
    markRenderPhaseStarted();
  }

  // 5. 执行同步工作循环 (Work Loop)
  //    这是 Fiber 树遍历和协调的核心。
  //    workLoopSync 会持续执行,直到 workInProgress 为 null (即整棵树处理完毕)
  //    或者遇到错误/挂起等情况。
  do {
    try {
      workLoopSync(); // 核心的同步工作循环
      break; // 如果 workLoopSync 正常完成 (没有抛出错误或被中断)
    } catch (thrownValue) {
      // 捕获在 workLoopSync 中发生的错误
      handleError(root, thrownValue);
    }
  } while (true);

  // 6. 清理工作
  //    - 重置执行上下文
  executionContext = prevExecutionContext;
  //    - 重置 Dispatcher
  ReactCurrentDispatcher.current = prevDispatcher;

  // 7. Profiler 相关逻辑 (如果开启)
  //    - 标记渲染结束
  if (__DEV__ && enableProfilerTimer) {
    markRenderPhaseStopped();
  }

  // 8. 根据 workInProgressRoot 的状态返回退出状态 (RootExitStatus)
  //    - 如果 workInProgressRoot.shellSuspendCounter > 0 (或其他挂起标记),返回 RootSuspended
  //    - 如果发生错误 (例如 root.thrownErrors 有内容),返回 RootErrored 或 RootFatalErrored
  //    - 否则,返回 RootCompleted

  if (workInProgressRootExitStatus !== RootInProgress) {
    // 如果在 workLoopSync 内部已经设置了退出状态 (例如通过 throw)
    return workInProgressRootExitStatus;
  }

  // 检查是否是由于 Suspense 导致的挂起
  // (这是一个简化的表示,实际逻辑更复杂,涉及 Suspense Boundaries 和 fallback)
  if (getWorkInProgressRootIsSuspended()) { // 假设有这样一个辅助函数
    // 可能需要标记哪些 lanes 被挂起,以及设置 ping
    // markRootSuspended(root, lanes);
    return RootSuspended;
  }

  // 检查是否有未捕获的错误
  if (root.thrownErrors !== null && root.thrownErrors.length > 0) {
    // 根据错误类型判断是 RootErrored 还是 RootFatalErrored
    return RootErrored; // 或 RootFatalErrored
  }

  // 如果一切顺利,返回完成状态
  return RootCompleted;
}

// 辅助函数 handleError (简化版)
function handleError(root: FiberRoot, thrownValue: any): void {
  // 重置 workInProgress 指针,以便从错误中恢复 (如果可能)
  resetContextDependencies();
  resetWorkInProgress();

  // 记录错误信息到 root 上
  // logError(root, thrownValue);
  // workInProgressRootExitStatus = RootErrored;

  // 向上查找错误边界 (Error Boundary)
  // throwAndUnwindWorkLoop(root, thrownValue);
  // 如果没有错误边界,或者错误发生在根节点,则标记为致命错误
}

// 核心同步工作循环 (简化版)
function workLoopSync(): void {
  // 只要 workInProgress (当前工作单元) 不为 null,就持续执行
  while (workInProgress !== null) {
    performUnitOfWork(workInProgress); // 处理单个 Fiber 节点
  }
}

renderRootSync 函数是 React Reconciler 中执行同步渲染的核心入口。当 React 决定以同步模式更新 UI 时(例如,在 Legacy 模式下,或通过 flushSync 调用,或处理高优先级的用户交互事件),就会调用此函数。它的主要职责是协调(Reconcile)Fiber 树,找出需要进行的更改,并为后续的提交阶段(Commit Phase)做准备。这个过程是不可中断的,会一次性完成。

其主要工作流程如下:

  1. 设置执行环境

    • 保存和设置执行上下文 (executionContext):函数首先会保存当前的 executionContext,然后将其设置为 RenderContext,表明 React 正处于渲染阶段。这有助于防止在渲染过程中发生不期望的重入行为。
    • 设置 Dispatcher (ReactCurrentDispatcher.current): 为了确保在渲染期间 Hooks(如 useState, useEffect)的行为符合预期(例如,在渲染期间调用 setState 应该同步处理或报错),会暂时将当前的 Dispatcher 替换为一个仅包含上下文 API 的 Dispatcher (ContextOnlyDispatcher) 或者一个适用于渲染阶段的特定 Dispatcher。
  2. 初始化或重置渲染状态

    • 准备工作栈 (prepareFreshStack): 这是非常关键的一步。如果当前的 workInProgressRoot 不是传入的 root,或者需要处理的 lanes 发生了变化,意味着这是一个新的渲染任务或现有任务的参数已改变。此时会调用 prepareFreshStack(root, lanes)
      • prepareFreshStack 会创建或复用当前 rootalternate Fiber 节点作为 workInProgressRoot(即正在构建的 Fiber 树的根)。
      • 它会重置 workInProgressRoot 的状态,例如清除之前的 laneseffects 等,并设置新的 renderLanes
      • 最重要的是,它会将 workInProgressRoot 设置为当前的工作单元 (workInProgress),作为 Fiber 树深度优先遍历的起点。
    • 全局/模块级变量设置: 一些用于跟踪渲染过程的全局或模块级变量会被初始化或更新,例如 workInProgressRoot (指向当前正在构建的树的根 Fiber)、workInProgressRootRenderLanes (本次渲染需要处理的 lanes)。
  3. Profiler 标记:如果 Profiler 功能被启用(通常在开发模式下),会调用相关的 API(如 markRenderPhaseStarted())来标记渲染阶段的开始,以便进行性能分析。

  4. 执行同步工作循环 (workLoopSync):这是 renderRootSync 的核心。它会启动一个循环,只要还有工作单元 (workInProgress 不为 null),就会持续执行。

    • 在循环内部,会调用 performUnitOfWork(workInProgress) 来处理当前的 Fiber 节点。performUnitOfWork 会根据 Fiber 节点的类型执行不同的操作(例如,对于 HostComponent,会比较 props 并生成 DOM 更新;对于 FunctionComponent,会调用函数组件本身)。
    • performUnitOfWork 执行完毕后,会返回下一个
    • 要处理的 Fiber 节点(通常是子节点或兄弟节点),并更新 workInProgress
    • 这个循环是同步的,会一直执行直到整棵 Fiber 树(或其需要更新的部分)都被处理完毕,即 workInProgress 变为 null
    • 错误处理: workLoopSync 通常被包裹在一个 try...catch 块中。如果在 performUnitOfWork 过程中发生错误(例如,组件渲染时抛出异常),catch 块会捕获这个错误,并调用 handleError 函数进行处理。handleError 可能会尝试寻找错误边界(Error Boundary)来优雅地处理错误,或者如果错误无法恢复,则标记整个渲染任务失败。
  5. 清理工作

    • 恢复执行上下文: 将 executionContext 恢复到进入 renderRootSync 之前的状态。
    • 恢复 Dispatcher: 将 ReactCurrentDispatcher.current 恢复到原始的 Dispatcher。
  6. Profiler 标记:如果 Profiler 开启,调用 markRenderPhaseStopped() 标记渲染阶段结束。

  7. 返回退出状态 (RootExitStatus):函数最后会根据渲染过程的结果返回一个状态码,告知调用者(如 performSyncWorkOnRoot)渲染的结果:

    • RootCompleted: 表示渲染成功完成,没有发生错误或挂起。
    • RootSuspended: 表示渲染过程中遇到了 Suspense 边界并且需要挂起(虽然在纯同步渲染中,挂起通常不期望发生,除非有特殊机制或与并发特性交互)。
    • RootErrored: 表示渲染过程中发生了可恢复的错误,并且可能已经被错误边界捕获。
    • RootFatalErrored: 表示发生了无法恢复的致命错误。
    • workInProgressRootExitStatus 是一个在 workLoopSync 内部可能被修改的变量,用于提前记录退出状态(例如,当错误发生时)。

renderRootSync 是 React 同步渲染机制的发动机。它通过一个严格的、不可中断的循环来构建或更新 Fiber 树,处理组件逻辑,并为最终的 DOM 更新(在 Commit 阶段)准备好所有必要的信息。其同步的特性保证了更新的即时性,但也意味着如果组件渲染逻辑复杂或耗时,可能会阻塞浏览器主线程。

renderRootConcurrent

renderRootConcurrent 函数解析
javascript
// renderRootConcurrent 函数 (位于 ReactFiberWorkLoop.js)
// 主要职责:以并发模式执行 Fiber 树的协调和渲染工作,允许任务中断和恢复。
function renderRootConcurrent(root: FiberRoot, lanes: Lanes): RootExitStatus {
  // 1. 设置执行上下文为 RenderContext
  const prevExecutionContext = executionContext;
  executionContext |= RenderContext;
  const prevDispatcher = pushDispatcher(root.containerInfo); // 设置 Dispatcher
  const prevAsyncDispatcher = pushAsyncDispatcher(); // 设置异步 Dispatcher

  // 2. 准备工作栈 (Work-in-progress Stack)
  if (workInProgressRoot !== root || workInProgressRootRenderLanes !== lanes) {
 Map 移动到 Set (movePendingFibersToMemoized)。

    // 获取当前渲染的 transitions
    workInProgressTransitions = getTransitionsForLanes(root, lanes);

    // 重置渲染计时器 (resetRenderTimer)
    resetRenderTimer();
    // 准备新的工作栈 (prepareFreshStack)
    prepareFreshStack(root, lanes);
  } else {
    // 如果是现有工作单元的延续,检查是否处于预渲染模式 (checkIfRootIsPrerendering)
    workInProgressRootIsPrerendering = checkIfRootIsPrerendering(root, lanes);
  }

  // 3. Profiler 相关逻辑 (如果开启)
  if (enableSchedulingProfiler) {
    markRenderStarted(lanes); // 标记渲染开始
  }

  // 4. 执行并发工作循环 (Concurrent Work Loop)
  outer: do {
    try {
      // 4.1 处理挂起状态 (Suspended State)
      if (workInProgressSuspendedReason !== NotSuspended && workInProgress !== null) {
        const unitOfWork = workInProgress;
        const thrownValue = workInProgressThrownValue;
        switch (workInProgressSuspendedReason) {
          case SuspendedOnError:
            // 解开栈 (Unwind) 然后继续正常的工作循环
            // ... throwAndUnwindWorkLoop ...
            break;
          case SuspendedOnData: // 数据挂起
          case SuspendedOnAction: // Action 挂起
            // 检查 thenable 是否已经 resolve
            // 如果已解决,则重播挂起的单元 (replaySuspendedUnitOfWork)
            // 如果未解决,则添加回调,在解决后标记 root 可继续并调度 root,然后跳出外层循环 (break outer)
            // ... thenable.then(onResolution, onResolution) ...
            break;
          case SuspendedOnImmediate: // 立即挂起 (通常为了给 ping 机会)
            // 标记为 SuspendedAndReadyToContinue,然后跳出外层循环 (break outer)
            break;
          case SuspendedOnInstance: // 实例挂起 (如 HostComponent 资源加载)
            // 标记为 SuspendedOnInstanceAndReadyToContinue,然后跳出外层循环 (break outer)
            break;
          case SuspendedAndReadyToContinue: // 已挂起并准备好继续
            // 检查 thenable 是否已解决
            // 如果已解决,重播挂起的单元 (replaySuspendedUnitOfWork)
            // 否则,解开栈并继续
            // ... throwAndUnwindWorkLoop ...
            break;
          case SuspendedOnInstanceAndReadyToContinue: // 实例已挂起并准备好继续
            // 检查资源 (如 preloadInstance) 是否已准备好
            // 如果已准备好,则恢复工作循环
            // 否则,解开栈并继续
            // ... throwAndUnwindWorkLoop ...
            break;
          case SuspendedOnDeprecatedThrowPromise: // 旧的 throw promise 模式
            // 总是解开栈
            // ... throwAndUnwindWorkLoop ...
            break;
          case SuspendedOnHydration: // Hydration 挂起
            // 重置工作栈,设置退出状态为 RootSuspendedAtTheShell,跳出外层循环
            break outer;
          default:
            // 抛出错误:未预期的挂起原因
            throw new Error('Unexpected SuspendedReason.');
        }
      }

      // 4.2 执行核心工作循环 (workLoopConcurrent)
    workLoopConcurrentByScheduler();
      break; // 如果 workLoopConcurrent 正常完成,跳出 do...while
    } catch (thrownValue) {
      // 4.3 捕获工作循环中发生的错误
      handleError(root, thrownValue);
    }
  } while (true);

  // 5. 清理工作
  executionContext = prevExecutionContext;
  //    - 重置 Dispatcher
  popDispatcher(prevDispatcher);
  popAsyncDispatcher(prevAsyncDispatcher);

  // 6. Profiler 相关逻辑 (如果开启)
  if (enableSchedulingProfiler) {
    if (workInProgressRootExitStatus === RootCompleted) {
      markRenderStopped(lanes); // 标记渲染成功结束
    } else if (workInProgressRootExitStatus === RootSuspended) {
      markSuspendedRenderPhase(lanes); // 标记渲染挂起
    } else if (workInProgressRootExitStatus === RootSuspendedWithDelay) {
      markSuspendedWithDelayPhase(lanes); // 标记渲染延迟挂起
    } else if (workInProgressRootExitStatus === RootErrored) {
      markErroredRenderPhase(lanes); // 标记渲染错误
    } else if (workInProgressRootExitStatus === RootFatalErrored) {
      markErroredRenderPhase(lanes); // 标记渲染致命错误
    } else if (workInProgressRootExitStatus === RootSuspendedAtTheShell) {
      markSuspendedRenderPhase(lanes); // 标记渲染在 Shell 处挂起
    } else {
      // 其他情况,如中断
      markInterruptedRenderPhase(lanes);
    }
  }

  // 7. 返回退出状态 (RootExitStatus)
  return workInProgressRootExitStatus;
}

renderRootConcurrent 函数是 React Reconciler 中用于以并发(Concurrent)模式执行 Root 更新的核心函数。与 renderRootSync 的同步、不可中断执行不同,renderRootConcurrent 旨在利用浏览器空闲时间,将渲染工作拆分为可中断的小单元,从而避免阻塞主线程,提升用户体验。

详细解析:

renderRootConcurrent 函数是 React 并发模式下执行渲染工作的核心入口。它的主要目标是在不阻塞浏览器主线程的情况下,高效地处理 UI 更新。其工作流程与 renderRootSync 有许多相似之处,但在核心工作循环 (workLoopConcurrentByScheduler) 和处理中断方面有所不同。

  1. 记录和设置执行上下文

    • renderRootSync 类似,函数首先保存当前的 executionContext,并将其设置为 RenderContext。这确保了在渲染过程中,React 知道自己处于一个渲染上下文中,并防止不必要的重入。
  2. 记录和设置调度器

    • 保存当前的 ReactCurrentDispatcher.current,并将其设置为 ContextOnlyDispatcher。这是 React Hooks 机制的一部分,确保 Hooks 只能在渲染阶段被调用。
  3. 初始化或重置渲染相关的全局/模块级变量 (prepareFreshStack)

    • 这是渲染准备阶段的关键一步。如果当前 rootlanes 与上次渲染不同,prepareFreshStack(root, lanes) 会被调用。
    • prepareFreshStack 的作用是初始化或重置与当前渲染任务相关的全局状态,包括:
      • 设置 workInProgressRoot 为当前 root
      • 设置 workInProgressRootRenderLanes 为当前需要处理的 lanes
      • 创建 workInProgress Fiber 树的根节点(通过 createWorkInProgress(root.current, null)),它是 current 树的一个克隆,所有渲染工作都将在这个 workInProgress 树上进行。
      • 重置其他与渲染过程相关的内部状态,如错误边界信息、Suspense 状态等,确保每次渲染都从一个干净的状态开始。
  4. Profiler 相关逻辑

    • 如果启用了 Profiler(通常在开发模式下),markRenderPhaseStarted() 会被调用,用于记录渲染阶段的开始时间,以便开发者工具进行性能分析。
  5. 执行并发工作循环 (workLoopConcurrentByScheduler)

    • 这是 renderRootConcurrent 的核心。与 workLoopSync 不同,workLoopConcurrentByScheduler 是一个可中断的循环。
    • 它会在每个时间切片(time slice)内执行一部分 Fiber 节点的协调工作。如果当前时间切片用尽,或者有更高优先级的任务到来,workLoopConcurrentByScheduler 会暂停执行,并将控制权交还给浏览器,等待下一次调度。
    • 这种机制使得 React 可以在不阻塞主线程的情况下,逐步完成复杂的 UI 更新,从而保持应用的响应性。
    • 循环内部包含 try-catch 块,用于捕获在协调过程中可能抛出的错误,并通过 handleError 进行处理。
  6. 清理工作

    • 渲染循环结束后,executionContext 会被恢复到进入 renderRootConcurrent 之前的状态。
    • ReactCurrentDispatcher.current 也会被重置,以防止 Hooks 在渲染阶段之外被错误调用。
  7. Profiler 相关逻辑

    • 如果启用了 Profiler,markRenderPhaseStopped() 会被调用,记录渲染阶段的结束时间。
  8. 根据 workInProgressRoot 的状态返回退出状态 (RootExitStatus)

    • renderRootConcurrent 会根据 workInProgressRoot 的最终状态返回一个 RootExitStatus,指示渲染的结果:
      • RootCompleted:表示渲染成功完成,可以进入提交阶段。
      • RootSuspended:表示渲染过程中遇到了 Suspense 组件,并且数据尚未准备好,渲染被挂起。此时,React 会等待数据加载完成后再继续渲染。
      • RootErroredRootFatalErrored:表示渲染过程中发生了错误。React 会根据错误的类型进行相应的处理,例如调度错误边界来显示错误 UI。

总结:

renderRootConcurrent 是 React 并发模式的核心驱动力。它通过时间切片和可中断的工作循环,实现了在保持 UI 响应性的同时处理复杂更新的能力。它与调度器紧密协作,确保渲染工作能够高效、平滑地进行,是 React 18+ 带来并发特性和 Suspense 的基石。

renderRootSyncrenderRootConcurrent 函数是 React 协调阶段的入口,它们的主要职责是设置渲染环境,并根据更新的类型(同步或并发)调用相应的工作循环函数 ( workLoopSyncworkLoopConcurrentByScheduler ) 来执行实际的 Fiber 树遍历和协调工作。 而 workLoopSyncworkLoopConcurrentByScheduler 函数实质上调用 performUnitOfWork 函数开启 Fiber 树的深度优先遍历,完成 Fiber 树的构建和更新。而这也是下一章节的重点。


微信公众号二维码