当我们执行完createRoot后,调用render展示组件,从updateContainer开始
updateContainer
graph TD
A[updateContainer入口] --> B[获取当前FiberRoot实例]
B --> C[获取事件时间requestEventTime]
C --> D[请求更新Lane通道requestUpdateLane]
D --> E[获取子树上下文getContextForSubtree]
E --> F[创建更新对象createUpdate]
F --> G["设置payload {element: ReactElement}"]
G --> H[处理回调函数]
H --> I[将更新加入队列enqueueUpdate]
I --> J[调度更新scheduleUpdateOnFiber]
J --> K[进入reconciler协调阶段]
style A fill:#f9f,stroke:#333
style K fill:#bbf,stroke:#333
updateContainer(element, container, parentComponent, callback){
// 获取FiberNode对象,这个对象代表了当前组件树的根节点
const current = container.current;
// 获取当前事件时间戳,距离系统启动时间
const eventTime = requestEventTime();
// 获取更新优先级
const lane = requestUpdateLane(current);
// 获取子树的上下文
const context = getContextForSubtree(parentComponent);
if (container.context === null) {
//初次渲染时设置容器上下文 上下文为空
container.context = context;
} else {
// 非首次渲染时将新上下文存入pendingContext,当组件下次更新时,pendingContext会被赋值给context
container.pendingContext = context;
}
//创建一个更新对象
const update = createUpdate(eventTime, lane);
// 负责将新的更新添加到Fiber节点的更新队列中
const root = enqueueUpdate(current, update, lane);
if (root !== null) {
// 调度更新
scheduleUpdateOnFiber(root, current, lane, eventTime);
entangleTransitions(root, current, lane);
}
return lane;
}
1.requestEventTime
- 时间原点机制
- 返回的是以应用启动时间为原点的相对时间戳(使用 performance.now() 实现),用于精确计算事件触发到实际处理的延迟时间
- 事件时间戳与当前渲染周期的 startTime 比较
- 事件时间戳与当前渲染周期的 startTime 比较
- 时间差越大(越早触发的更新),优先级越高
- 时间差越小(越晚触发的更新),优先级越低
- 时间差为 0 (同一时间触发的更新),优先级相同
- 批量更新优化
- 同一事件周期内的多个更新会合并为单个调度
- 事件时间戳作为合并的标识符(同一时间窗口内的更新共享一个批次)
- 事件时间戳相同的更新会被合并到同一个批次中
- 事件时间戳不同的更新会被独立调度
executionContext:代表渲染期间的上下文
RenderContext:表示当前是否处于渲染阶段。
CommitContext:代表渲染期间的执行上下文,是提交更新的阶段,这个阶段发生在React的渲染周期中,用于处理组件更新后的最终提交操作
graph TD
A[开始] --> B{是否在React上下文并且处于RenderContext或者 CommitContext}
B -->|是| C[返回now]
B -->|否| D{currentEventTime是否为NoTimestamp}
D -->|否| E[返回currentEventTime]
D -->|是| F[设置currentEventTime=now]
F --> G[返回currentEventTime]
export function requestEventTime() {
if ((executionContext & (RenderContext | CommitContext)) !== NoContext) {
// 处于react上下文,存在正在render和commit的事件 正处于render和commit阶段
// 返回当前时间
// We're inside React, so it's fine to read the actual time.
return now();
}
//不再react上下文,可能存在浏览器事件中
// We're not inside React, so we may be in the middle of a browser event.
if (currentEventTime !== NoTimestamp) {
// 可能是多次同步执行更新,知道下一次更新,均使用相同时间
// 返回当前事件时间
// Use the same start time for all updates until we enter React again.
return currentEventTime;
}
// 第一次更新的时候
// 之前的任务已经执行完,开启新的任务时候需要重新计算时间
// This is the first update since React yielded. Compute a new start time.
currentEventTime = now();
return currentEventTime;
}
2.requestUpdateLane
计算更新车道函数。React中有一个Lane的概念,意为车道,具有不同的优先级,通过通过一个32位的二进制数来表示,数值越小表示优先级越高。 同的Lane代表不同的任务,最终转换为调度优先级。
常见的lane有
type Lane = number;
const SyncLane = 0b0000000000000000000000000000001; // 最高优先级
const InputContinuousLane = 0b0000000000000000000000000000100;
const DefaultLane = 0b0000000000000000000000000010000;
const currentEventTransitionLane = NoLanes = 0b0000000000000000000000000000000
...
该函数实现了 React 的优先级调度策略,通过车道(Lane)模型实现:
- 高优先级更新可中断低优先级工作
- 批量更新合并(相同车道更新合并)
- 饥饿问题预防(长时间未处理的车道会提升优先级)
- 动态优先级调整(根据事件类型动态分配优先级)
- 过渡更新(Transition)处理(在过渡期间的更新)
- 同步模式处理(立即执行)
- 异步模式处理(根据优先级调度)
function requestUpdateLane(fiber: Fiber): Lane {
if ((fiber.mode & ConcurrentMode) === NoMode) {
// 同步模式处理
// 当 Fiber 树处于同步模式(ConcurrentMode 关闭)时,直接返回最高优先级的同步车道(SyncLane),确保立即执行
return (SyncLane: Lane);
}else if (
!deferRenderPhaseUpdateToNextBatch &&
(executionContext & RenderContext) !== NoContext &&
workInProgressRootRenderLanes !== NoLanes
) {
// render 阶段
//返回最高优先级车道
return pickArbitraryLane(workInProgressRootRenderLanes);
}
// 过渡(Transition)更新处理
const isTransition = requestCurrentTransition() !== NoTransition;
if (isTransition) {
if (currentEventTransitionLane === NoLane) {
//不存在过度更新车道,为多个并发过渡更新间循环分配独立车道
currentEventTransitionLane = claimNextTransitionLane();
}
// 当存在过渡更新(Transition)时,返回当前事件的过渡车道
return currentEventTransitionLane;
}
// 当存在当前更新优先级(由 flushSync 等 API 提供)时,返回该优先级的车道
const updateLane: Lane = (getCurrentUpdatePriority(): any);
if (updateLane !== NoLane) {
return updateLane;
}
// 通过 getCurrentEventPriority,根据宿主环境的事件类型(如鼠标点击、键盘输入等),分配对应的优先级车道(InputContinuousLane、DefaultLane 等)
const eventLane: Lane = (getCurrentEventPriority(): any);
return eventLane;
}
3.getContextForSubtree
获取子树的上下文
graph TD
A[调用 getContextForSubtree] --> B{parentComponent 存在?}
B -- 否 --> C[返回 emptyContextObject]
B -- 是 --> D[获取Fiber节点]
D --> E[获取当前未屏蔽Context]
E --> F{Fiber类型为ClassComponent?}
F -- 否 --> G[返回父级Context]
F -- 是 --> H{是否为旧版Context提供者?}
H -- 否 --> G
H -- 是 --> I[处理子Context继承]
I --> J[返回处理后的Context]
function getContextForSubtree(
parentComponent: ?React$Component<any, any>,
): Object {
// 如果父级为空,则返回空上下文
if (!parentComponent) {
return emptyContextObject;
}
// 获取父组件的实例fiber
const fiber = getInstance(parentComponent);
// 获取当前未屏蔽的上下文
const parentContext = findCurrentUnmaskedContext(fiber);
//如果父组件是类组件
if (fiber.tag === ClassComponent) {
const Component = fiber.type;
//判断是否为旧版Context提供者
if (isLegacyContextProvider(Component)) {
//子组件继承
return processChildContext(fiber, Component, parentContext);
}
}
return parentContext;
}
4.createUpdate
//一个更新对象,用于描述一个更新操作,包含了更新的内容、优先级、回调函数等信息
- 以下为对象结构
const update: Update<*> = {
eventTime, // 事件时间戳(来自performance.now())
lane, // 更新所属的优先级通道
tag: UpdateState, // 更新类型标识(0=普通状态更新)
payload: null, // 实际更新内容(可以是对象或函数)
callback: null, // 更新完成后的回调函数
next: null, // 指向下一个更新对象的指针,构成循环链表的关键指针
};
其中tagd的种类如下
- UpdateState =0 ==> 普通状态更新
- ReplaceState = 1 ==> 替换整个状态
- ForceUpdate = 2 ==> 强制重新渲染
- CaptureUpdate =3 ==> 错误边界捕获的更新
5.enqueueUpdate
我们获取完事件时间、车道级别、子树上下文之后,创建了更新对象,需要通过enqueueUpdate插入更新队列函数将其插入更新队列。
该函数接收一个fiber、update对象和lane(优先级通道),然后将update加入到共享队列中,根据isUnsafeClassRenderPhaseUpdate 标志来判断更新是否在类组件的渲染阶段,来决定不同的管理和调度
export function enqueueUpdate<State>(
fiber: Fiber,
update: Update<State>,
lane: Lane,
): FiberRoot | null {
// 获取当前fiber的更新队列
const updateQueue = fiber.updateQueue;
if (updateQueue === null) {
// 已卸载的fiber直接返回
return null;
}
// 获取共享队列(current与WIP共享)
const sharedQueue: SharedQueue<State> = (updateQueue: any).shared;
// 类组件渲染阶段处理 并不安全 比如render阶段setState
// 类组件的更新队列是一个环形链表,每个更新对象都有一个 next 指针指向下一个更新对象
if (isUnsafeClassRenderPhaseUpdate(fiber)) {
// 直接操作循环链表
const pending = sharedQueue.pending;
if (pending === null) {
update.next = update; // 创建自引用循环链表
} else {
update.next = pending.next;
pending.next = update;
}
sharedQueue.pending = update; // 更新队列头指针
// 优先级标记
return unsafe_markUpdateLaneFromFiberToRoot(fiber, lane);
} else {
// 并发组件渲染模式
return enqueueConcurrentClassUpdate(fiber, sharedQueue, update, lane);
}
}
6.enqueueConcurrentClassUpdate
enqueueConcurrentClassUpdate实际调用的是enqueueUpdate,主要功能是将更新暂存到全局队列中以便后续批量处理,保证渲染过程可中断
触发更新 → enqueueUpdate(暂存) → finishQueueingConcurrentUpdates(批量处理) → 调度更新 → 渲染
function enqueueConcurrentClassUpdate<State>(
fiber: Fiber,
queue: ClassQueue<State>,
update: ClassUpdate<State>,
lane: Lane,
): FiberRoot | null {
const concurrentQueue: ConcurrentQueue = (queue: any);
const concurrentUpdate: ConcurrentUpdate = (update: any);
enqueueUpdate(fiber, concurrentQueue, concurrentUpdate, lane);
return getRootForUpdatedFiber(fiber);
}
function enqueueUpdate(
fiber: Fiber,
queue: ConcurrentQueue | null,
update: ConcurrentUpdate | null,
lane: Lane,
) {
//concurrentQueues 是一个全局的扁平化数组 ,按顺序存储更新的相关信息:
//每个更新占用数组中的 4 个连续位置: [fiber, queue, update, lane] 比如 fiber、队列、更新级别、车道
concurrentQueues[concurrentQueuesIndex++] = fiber;
concurrentQueues[concurrentQueuesIndex++] = queue;
concurrentQueues[concurrentQueuesIndex++] = update;
concurrentQueues[concurrentQueuesIndex++] = lane;
// 合并更新通道(优先级处理)
concurrentlyUpdatedLanes = mergeLanes(concurrentlyUpdatedLanes, lane);
// 更新 fiber 的 lanes 字段
fiber.lanes = mergeLanes(fiber.lanes, lane);
//同步备用节点(alternate)的 lanes
const alternate = fiber.alternate;
//双树协调,同时更新当前 fiber 和 alternate fiber 的 lanes 字段,保持双 fiber 树状态同步
if (alternate !== null) {
alternate.lanes = mergeLanes(alternate.lanes, lane);
}
}
1.双树结构 React 中的 Fiber 树是一个双树结构,每个 Fiber 节点都有两个指针:return 和 child。
- return 指向父节点,用于构建树状结构
- child 指向子节点,用于遍历子节点
2.双树同步 当一个更新触发时,React 会同时更新当前 Fiber 树和备用 Fiber 树(alternate)。
- 根节点的 current 指向当前 Fiber 树
- 根节点的 alternate 指向备用 Fiber 树
3.优先级标记 每个 Fiber 节点都有一个 lanes 属性,用于记录当前节点的更新优先级。
- 当一个更新触发时,会将更新的优先级标记到当前 Fiber 节点和备用 Fiber 节点的 lanes 中。
- 这样,在渲染过程中,React 可以根据 lanes 来决定是否中断或跳过某些节点的渲染。
mergeLanes 是一个位或操作合并车道方法,按位或运算在这里的作用是将两个优先级集合合并,形成一个包含两者所有优先级的新集合。例如,如果a是0b0001(SyncLane),b是0b0010(InputContinuousLane),合并后的结果就是0b0011,表示同时包含这两个优先级。
该函数是 React 并发模式(Concurrent Mode)实现细粒度调度的基础,通过位掩码操作高效管理复杂优先级关系。 组件多次 setState 的优先级合并 等会用到此方法
function mergeLanes(a: Lanes | Lane, b: Lanes | Lane): Lanes {
return a | b;
}
getRootForUpdatedFiber这个函数的作用是当发生状态更新(如setState)时,确保根节点(root)被正确调度,通过遍历fiber树的返回路径来找到根节点
- 确保更新能够正确冒泡到根节点
- 为调度器提供更新入口点
- 防止在未完成挂载的组件上执行无效更新
- 维护 fiber 树结构的完整性
graph TD
A[sourceFiber] --> B{parent存在?}
B -->|是| C[检测未挂载状态]
C --> D[移动至父节点]
D --> B
B -->|否| E[检查HostRoot标签]
E -->|是| F[返回FiberRoot]
E -->|否| G[返回null]
function getRootForUpdatedFiber(sourceFiber: Fiber): FiberRoot | null {
// 开发环境检测未挂载组件上的更新
detectUpdateOnUnmountedFiber(sourceFiber, sourceFiber);
// 通过父节点链向上遍历
let node = sourceFiber;
let parent = node.return;
while (parent !== null) {
detectUpdateOnUnmountedFiber(sourceFiber, node);
node = parent;
parent = node.return;
}
// 返回 HostRoot 节点的 stateNode(即 FiberRoot)
return node.tag === HostRoot ? (node.stateNode: FiberRoot) : null;
}
通过 fiber.return 指针逐级向上查找,直到 HostRoot 节点,返回的 FiberRoot 包含整个应用的调度信息
当我们将更新队列创建完并且插入全局后,就要开始通过scheduleUpdateOnFiber调度更新了,他是所有任务更新的唯一入口
7.scheduleUpdateOnFiber 调度更新
这个函数主要负责在Fiber 根节点上调度更新,处理不同的更新场景,比如渲染阶段、被动效果阶段等
在react应用中,调用setState、useState会触发该函数,父组件重新渲染或者context变化也会触发
最后通过调用ensureRootIsScheduled来安排根节点的任务,并在同步车道且无执行上下文时,立即刷新同步回调
如果处于渲染阶段,则合并车道,否则直接调度更新根节点
用户操作/状态变更 → scheduleUpdateOnFiber → 更新调度 → 渲染流程
↓
协调同步/异步更新、处理不同优先级任务
graph TD
A[开始] --> B{渲染阶段?}
B -->|是| C[记录渲染阶段更新]
B -->|否| D[处理正常更新]
D --> E[开发工具追踪]
E --> F[性能分析回调]
F --> G[过渡追踪处理]
G --> H[合并交错更新]
H --> I[调度更新任务]
I --> J{同步模式?}
J -->|是| K[立即执行回调]
J -->|否| L[结束]
export function scheduleUpdateOnFiber(
root: FiberRoot,
fiber: Fiber,
lane: Lane,
eventTime: number,
) {
// 1. 嵌套更新检查(防止无限循环)
// NESTED_UPDATE_LIMIT 默认50次
checkForNestedUpdates();
// 开发模式错误检测
if (__DEV__) {
// 禁止在useInsertionEffect中调度更新
if (isRunningInsertionEffect) {
console.error('useInsertionEffect must not schedule updates.');
}
// 标记被动效果阶段的更新
if (isFlushingPassiveEffects) {
didScheduleUpdateDuringPassiveEffects = true;
}
}
// 2. 标记根节点有待处理更新
markRootUpdated(root, lane, eventTime);
// 3. 判断更新类型
// 通过executionContext 和 RenderContext判断是否处于渲染阶段
if (
(executionContext & RenderContext) !== NoLanes &&
root === workInProgressRoot
) {
/ 此更新在渲染阶段被派发。如果更新来源于用户代码(本地Hook更新除外,
// 这些更新有不同处理方式且不会到达此函数),这属于错误行为。但React
// 内部某些特性会将其作为实现细节使用,例如选择性水合(selective hydration)。
//比如在组件 render 方法或 Hook 中调用 setState 等更新 API
warnAboutRenderPhaseUpdatesInDEV(fiber); // 开发警告
workInProgressRootRenderPhaseUpdatedLanes = mergeLanes(...); // 合并车道
} else {
// 这是常规更新,在渲染阶段之外被调度。例如,在输入事件处理过程中。
// 常规更新比如,由用户交互或副作用触发的标准状态更新
if (enableUpdaterTracking) {
addFiberToLanesMap(root, fiber, lane);
}
// Profiler性能追踪
if (enableProfilerTimer) {
// 遍历父节点寻找Profiler组件
let current = fiber;
while (current !== null) {
if (current.tag === Profiler) {
const {id, onNestedUpdateScheduled} = current.memoizedProps;
if (typeof onNestedUpdateScheduled === 'function') {
onNestedUpdateScheduled(id); // 触发回调
}
}
current = current.return;
}
}
// 过渡追踪处理
if (enableTransitionTracing) {
const transition = ReactCurrentBatchConfig.transition;
if (transition !== null) {
transition.startTime = now(); // 记录开始时间
addTransitionToLanesMap(root, transition, lane);
}
}
// 4. 处理中间渲染的更新
if (root === workInProgressRoot) {
// 合并交错更新车道
workInProgressRootInterleavedUpdatedLanes = mergeLanes(...);
// 处理挂起状态的根
if (workInProgressRootExitStatus === RootSuspendedWithDelay) {
markRootSuspended(root, workInProgressRootRenderLanes);
}
}
// 5. 调度根节点更新
ensureRootIsScheduled(root, eventTime);
// 6. 同步模式优化处理
if (lane === SyncLane && executionContext === NoContext) {
resetRenderTimer();
flushSyncCallbacksOnlyInLegacyMode(); // 立即执行同步回调
}
}
}