React19源码解析0:整体流程速通

作者:renzp94
时间:2025-04-16 14:00:00

速通总结

初始化阶段通过createRoot函数创建FiberRoot (应用全局对象)RootFiber (根 Fiber 节点)及初始化事件系统,并创建并返回ReactDOMRoot实例

然后通过root.render函数进入render阶段,在此阶段通过beginWork阶段Fiber节点树向下处理,主要操作是创建/复用Fiber节点,执行组件渲染,标记需要更新的副作用。然后是completeWork阶段向上完成处理节点,主要操作是创建DOM节点设置DOM属性和事件,收集副作用到effect list,这两个阶段也叫做递归处理。

render阶段结束后则进入commit阶段,此阶段主要是根据节点变化进行DOM变更,在此阶段通过Before Mutation阶段获取DOM变更前的状态,然后通过Mutation阶段执行实际DOM操作(增删改),还有一个After Mutation阶段主要是做Mutation阶段的收尾处理,如果有View Transition动画则触发View Transition动画。最后再执行Layout阶段进行DOM元素的绘制。 当DOM绘制之后如果有副作用(effect)则会异步调用副作用处理。

应用首次启动

初始化阶段

import { createRoot } from 'react-dom/client'

const root = createRoot(document.querySelector('#root'))

React应用启动时,会调用react-dom包中的createRoot函数。

createRoot函数会做如下处理:

  • 验证容器有效性
  • 通过react-reconciler包中的createContainer创建FiberRoot (应用全局对象)RootFiber (根 Fiber 节点)
  • 通过react-dom-bindings包中的listenToAllSupportedEvents函数初始化事件系统
  • 创建并返回ReactDOMRoot实例

验证容器的有效性

if (!isValidContainer(container)) {
  throw new Error('Target container is not a DOM element.');
}

源码定位

创建FiberRoot(应用全局对象)RootFiber(根Fiber节点)

调用react-reconciler包中的createContainer函数创建FiberRoot(应用全局对象)RootFiber(根Fiber节点)

const root = createContainer(
  container,
  ConcurrentRoot,
  null,
  isStrictMode,
  concurrentUpdatesByDefaultOverride,
  identifierPrefix,
  onUncaughtError,
  onCaughtError,
  onRecoverableError,
  transitionCallbacks,
);

源码定位

然后调用react-dom-bindings包中的markContainerAsRootRootFiberDOM元素关联

markContainerAsRoot(root.current, container);

源码定位

初始化事件系统

调用react-dom-bindings包中的listenToAllSupportEvents函数初始化事件系统

listenToAllSupportedEvents(rootContainerElement);

源码定位

创建ReactDOMRoot实例并返回

创建并返回ReactDOMRoot实例

return new ReactDOMRoot(root);

源码定位

render阶段

调用ReactDOMRoot对象render函数来触发首次更新调度和渲染。

import App from './App'

root.render(<App />)

调用updateContainer函数

render函数中会调用react-reconciler包中的updateContainer函数。

updateContainer(children, root, null, null);

源码定位

调度更新前处理

updateContainer函数中调用createUpdate函数创建更新对象

const update = createUpdate(lane);
update.payload = {element};

源码定位

然后调用enqueueUpdate函数将更新对象放入更新队列中

const root = enqueueUpdate(rootFiber, update, lane);

源码定位

最后调用scheduleUpdateOnFiber函数开始调度更新,进入调度阶段。

scheduleUpdateOnFiber(root, rootFiber, lane);

源码定位

调度阶段(Schedule)

调用markRootUpdated函数标记更新优先级

markRootUpdated(root, lane);

源码定位

调用ensureRootIsScheduled函数确保根节点被调度。

ensureRootIsScheduled(root);

源码定位

ensureRootIsScheduled函数中调用scheduleImmediateRootScheduleTask函数。

scheduleImmediateRootScheduleTask();

源码定位

scheduleImmediateRootScheduleTask函数中调用scheduleTaskForRootDuringMicrotask函数。

const nextLanes = scheduleTaskForRootDuringMicrotask(root, currentTime);

源码定位

scheduleTaskForRootDuringMicrotask函数中,绑定performWorkOnRootViaSchedulerTask函数到FiberRoot实例上,并通过scheduleCallback将函数放到调度任务队列中,等到主进程空闲时调用。

const newCallbackNode = scheduleCallback(
  schedulerPriorityLevel,
  performWorkOnRootViaSchedulerTask.bind(null, root),
);

源码定位

最终通过调用performWorkOnRoot函数进入渲染阶段

performWorkOnRoot(root, lanes, forceSync);

源码定位

渲染阶段(render阶段)

performWorkOnRoot函数中调用renderRootConcurrent进行并发渲染。

let exitStatus = shouldTimeSlice
  ? renderRootConcurrent(root, lanes)
  : renderRootSync(root, lanes, true);

源码定位

renderRootConcurrent函数中会调用workLoopConcurrent函数或者workLoopConcurrentByScheduler函数

} else if (enableThrottledScheduling) {
  workLoopConcurrent(includesNonIdleWork(lanes));
} else {
  workLoopConcurrentByScheduler();
}

源码定位

workLoopConcurrent函数或workLoopConcurrentByScheduler函数中最终都会调用performUnitOfWork函数,

performUnitOfWork(workInProgress);

源码定位

performUnitOfWork函数中调用beginWork函数,开始beginWork阶段

next = beginWork(current, unitOfWork, entangledRenderLanes);

源码定位

beginWork

switch (workInProgress.tag) {
  case LazyComponent: ...
  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,
    );
  }
  ...
}

源码定位

beginWork函数中判断workInProgress(构建Fiber树)的类型来进行响应的操作,常见的有几个:

  • LazyComponent:处理React.lazy()创建的懒加载组件,调用mountLazyComponent进行加载和渲染。
  • FunctionComponent:处理函数组件,解析默认props后调用updateFunctionComponent进行更新。
  • ClassComponent:处理类组件,解析props后调用updateClassComponent进行更新。
  • SuspenseComponent:处理Suspense组件,管理异步加载状态和fallback UI
  • ForwardRef: 处理forwardRef组件,解析props后调用updateForwardRef进行更新。
  • ContextProvider/ContextConsumer: 处理Context相关的ProviderConsumer组件。
  • MemoComponent/SimpleMemoComponent: 处理memo组件,进行props浅比较决定是否跳过更新。

上面传入的App组件是一个函数组件,所以将会调用updateFunctionComponent函数。

updateFunctionComponent函数中会先调用renderWithHooks函数生成React元素树

nextChildren = renderWithHooks(
  current,
  workInProgress,
  Component,
  nextProps,
  context,
  renderLanes,
);

源码定位

然后reconcileChildren函数对比current Fiber树(当前页面渲染内容的Fiber树)workInProgress Fiber树(正在构建的Fiber树) 并标记需要插入/更新/删除的节点。

reconcileChildren(current, workInProgress, nextChildren, renderLanes);

源码定位

completeWork

如果在beginWork阶段返回了null则调用completeUnitOfWork函数完成当前节点的处理。

if (next === null) {
  // If this doesn't spawn new work, complete the current work.
  completeUnitOfWork(unitOfWork);
} else {
  workInProgress = next;
}

源码定位

completeUnitOfWork函数中会调用completeWork函数进行处理。

next = completeWork(current, completedWork, entangledRenderLanes);

源码定位

总结

整体来看,渲染阶段主要有两个阶段:beginWorkcompleteWork

  • beginWork: 主要是向下处理节点,创建/复用Fiber节点,执行组件渲染,标记需要更新的副作用。
  • completeWork: 主要是向上完成节点,创建DOM节点设置DOM属性和事件,收集副作用到effect list

commit阶段

finishConcurrentRender(
  root,
  exitStatus,
  finishedWork,
  lanes,
  renderEndTime,
);

源码定位

执行完renderRootConcurrent函数后会调用finishConcurrentRender进入commit阶段

finishConcurrentRender函数中会调用commitRootWhenReady函数

commitRootWhenReady(
  root,
  finishedWork,
  workInProgressRootRecoverableErrors,
  workInProgressTransitions,
  workInProgressRootDidIncludeRecursiveRenderUpdate,
  lanes,
  workInProgressDeferredLane,
  workInProgressRootInterleavedUpdatedLanes,
  workInProgressSuspendedRetryLanes,
  workInProgressRootDidSkipSuspendedSiblings,
  exitStatus,
  IMMEDIATE_COMMIT,
  renderStartTime,
  renderEndTime,
);

源码定位

commitRootWhenReady函数主要是做commit的前置校验,最终会调用commitRoot函数执行真正的操作。

commitRoot(
  root,
  finishedWork,
  lanes,
  recoverableErrors,
  transitions,
  didIncludeRenderPhaseUpdate,
  spawnedLane,
  updatedLanes,
  suspendedRetryLanes,
  exitStatus,
  suspendedCommitReason,
  completedRenderStartTime,
  completedRenderEndTime,
);

源码定位

Before Mutation

调用commitBeforeMutationEffects函数来进行Before Mutation阶段的处理。

commitBeforeMutationEffects(root, finishedWork, lanes);

源码定位

commitBeforeMutationEffects函数最终会调用commitBeforeMutationEffectsOnFiber函数。

focusedInstanceHandle = prepareForCommit(root.containerInfo);
shouldFireAfterActiveInstanceBlur = false;
const isViewTransitionEligible =
  enableViewTransition &&
  includesOnlyViewTransitionEligibleLanes(committedLanes);
nextEffect = firstChild;
commitBeforeMutationEffects_begin(isViewTransitionEligible);
// We no longer need to track the active instance fiber
focusedInstanceHandle = null;
// We've found any matched pairs and can now reset.
resetAppearingViewTransitions();

源码定位

commitBeforeMutationEffectsOnFiber(fiber, isViewTransitionEligible);

源码定位

commitBeforeMutationEffectsOnFiber函数中主要做当前Fiber节点的处理。

switch (finishedWork.tag) {
  case FunctionComponent: {
    if (enableUseEffectEventHook) {
      if ((flags & Update) !== NoFlags) {
        const updateQueue: FunctionComponentUpdateQueue | null =
          (finishedWork.updateQueue: any);
        const eventPayloads =
          updateQueue !== null ? updateQueue.events : null;
        if (eventPayloads !== null) {
          for (let ii = 0; ii < eventPayloads.length; ii++) {
            const {ref, nextImpl} = eventPayloads[ii];
            ref.impl = nextImpl;
          }
        }
      }
    }
    break;
  }
  ...

源码定位

主要功能点

  1. 焦点管理
    • 当 Suspense 边界从显示变为隐藏时
    • 检查当前聚焦元素是否在隐藏的子树中
    • 触发 blur 事件保证焦点正确转移
  2. 组件特定处理
    • 函数组件 :更新 useEffectEvent 的引用
    • 类组件 :调用 getSnapshotBeforeUpdate 生命周期
    • HostRoot :清空容器为后续渲染做准备
    • ViewTransition :处理视图过渡动画的准备工作
  3. 错误处理
    • 验证不支持的 Snapshot 标志
    • 抛出明确错误信息帮助调试

特殊处理

  • 视图过渡优化 当启用视图过渡且当前更新符合条件时,会额外处理视图过渡相关的标记和状态
  • 性能优化 通过 flags 系统避免不必要的遍历和计算

调用时机

该函数在以下阶段被调用:

  1. commitBeforeMutationEffects 遍历 Fiber 树时
  2. 在 mutation 阶段(DOM 变更)之前
  3. 在 layout 阶段(生命周期)之前

注意事项

  • 该阶段不应执行任何 DOM 变更
  • 所有操作必须是同步且无副作用的
  • 错误会中断整个提交过程

Mutation阶段

commitBeforeMutationEffects函数执行后会调用flushMutationEffects函数来进入Mutation阶段

pendingEffectsStatus = PENDING_MUTATION_PHASE;
if (enableViewTransition && willStartViewTransition) {
  pendingViewTransition = startViewTransition(
    root.containerInfo,
    pendingTransitionTypes,
    flushMutationEffects,
    flushLayoutEffects,
    flushAfterMutationEffects,
    flushSpawnedWork,
    flushPassiveEffects,
    reportViewTransitionError,
  );
} else {
  // Flush synchronously.
  flushMutationEffects();
  flushLayoutEffects();
  // Skip flushAfterMutationEffects
  flushSpawnedWork();
}

源码定位

flushMutationEffects函数中会调用commitMutationEffects函数,然后在其中调用commitMutationEffectsOnFiber进行commit操作

commitMutationEffects(root, finishedWork, lanes);

源码定位

commitMutationEffectsOnFiber(finishedWork, root, committedLanes);

源码定位

switch (finishedWork.tag) {
    case FunctionComponent:
    case ForwardRef:
    case MemoComponent:
    case SimpleMemoComponent: {
      recursivelyTraverseMutationEffects(root, finishedWork, lanes);
      commitReconciliationEffects(finishedWork, lanes);

      if (flags & Update) {
        commitHookEffectListUnmount(
          HookInsertion | HookHasEffect,
          finishedWork,
          finishedWork.return,
        );
        // TODO: Use a commitHookInsertionUnmountEffects wrapper to record timings.
        commitHookEffectListMount(HookInsertion | HookHasEffect, finishedWork);
        commitHookLayoutUnmountEffects(
          finishedWork,
          finishedWork.return,
          HookLayout | HookHasEffect,
        );
      }
      break;
    }
    ...

源码定位

commitMutationEffectsOnFiber函数主要处理:

  • 递归处理子节点变更
  • 执行当前节点的协调效果
  • 根据flags执行特定操作
  • 处理ref和回调

Layout阶段

执行完Mutation阶段则调用flushLayoutEffects函数进入Layout阶段,此阶段执行时机是:在DOM变更(mutation)完成后,浏览器绘制前执行。

pendingEffectsStatus = PENDING_MUTATION_PHASE;
if (enableViewTransition && willStartViewTransition) {
  pendingViewTransition = startViewTransition(
    root.containerInfo,
    pendingTransitionTypes,
    flushMutationEffects,
    flushLayoutEffects,
    flushAfterMutationEffects,
    flushSpawnedWork,
    flushPassiveEffects,
    reportViewTransitionError,
  );
} else {
  // Flush synchronously.
  flushMutationEffects();
  flushLayoutEffects();
  // Skip flushAfterMutationEffects
  flushSpawnedWork();
}

源码定位

flushLayoutEffects函数中会调用commitLayoutEffects函数,commitLayoutEffects函数会调用commitLayoutEffectOnFiber函数执行真正的处理操作。

commitLayoutEffects(finishedWork, root, lanes);

源码定位

commitLayoutEffectOnFiber(root, current, finishedWork, committedLanes);

源码定位

switch (finishedWork.tag) {
  case FunctionComponent:
  case ForwardRef:
  case SimpleMemoComponent: {
    recursivelyTraverseLayoutEffects(
      finishedRoot,
      finishedWork,
      committedLanes,
    );
    if (flags & Update) {
      commitHookLayoutEffects(finishedWork, HookLayout | HookHasEffect);
    }
    break;
  }
  ...

源码定位

此函数主要是

  • 调用类组件的componentDidMountcomponentDidUpdate生命周期
  • 处理函数组件的useLayoutEffect钩子
  • 执行ref回调

其他阶段

最后还有几个收尾的阶段

  • After Mutation阶段:进行Mutation阶段的收尾处理,如果有View Transition动画则触发View Transition动画
  • Spawned Work 阶段:请求浏览器绘制,检查是否有passive effects需要处理
  • Passive Effects 阶段:异步调度useEffect回调(passive effects)