React19源码解析0:整体流程速通
速通总结
在
初始化阶段
通过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
包中的markContainerAsRoot
将RootFiber
和DOM元素
关联
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
相关的Provider
和Consumer
组件。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);
总结
整体来看,渲染阶段主要有两个阶段:beginWork
和completeWork
。
- 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;
}
...
主要功能点
- 焦点管理
- 当 Suspense 边界从显示变为隐藏时
- 检查当前聚焦元素是否在隐藏的子树中
- 触发 blur 事件保证焦点正确转移
- 组件特定处理
- 函数组件 :更新 useEffectEvent 的引用
- 类组件 :调用 getSnapshotBeforeUpdate 生命周期
- HostRoot :清空容器为后续渲染做准备
- ViewTransition :处理视图过渡动画的准备工作
- 错误处理
- 验证不支持的 Snapshot 标志
- 抛出明确错误信息帮助调试
特殊处理
- 视图过渡优化 当启用视图过渡且当前更新符合条件时,会额外处理视图过渡相关的标记和状态
- 性能优化 通过 flags 系统避免不必要的遍历和计算
调用时机
该函数在以下阶段被调用:
- commitBeforeMutationEffects 遍历 Fiber 树时
- 在 mutation 阶段(DOM 变更)之前
- 在 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;
}
...
此函数主要是
- 调用类组件的
componentDidMount
和componentDidUpdate
生命周期 - 处理函数组件的
useLayoutEffect
钩子 - 执行
ref
回调
其他阶段
最后还有几个收尾的阶段
After Mutation阶段
:进行Mutation阶段
的收尾处理,如果有View Transition动画
则触发View Transition动画
。Spawned Work 阶段
:请求浏览器绘制,检查是否有passive effects
需要处理Passive Effects 阶段
:异步调度useEffect
回调(passive effects)