上一篇我们讲到了,root.render()最后调用scheduleUpdateOnFiber注册开始任务调度,本篇我们介绍下scheduleUpdateOnFiber做了什么
scheduleUpdateOnFiber 函数的作用
scheduleUpdateOnFiber 是 React 调度系统的核心入口函数,它负责将更新转换为可执行的任务并进行调度。
函数签名
js
export function scheduleUpdateOnFiber(
root: FiberRoot, // Fiber 根节点
fiber: Fiber, // 发生更新的 Fiber 节点
lane: Lane, // 更新的优先级
eventTime: number, // 事件时间戳
)
主要工作步骤
第一步:安全检查和标记
js
checkForNestedUpdates(); // 检查嵌套更新
markRootUpdated(root, lane, eventTime); // 标记根节点有更新
第二步:区分渲染阶段更新 vs 正常更新
js
if (
(executionContext & RenderContext) !== NoLanes &&
root === workInProgressRoot
) {
warnAboutRenderPhaseUpdatesInDEV(fiber);
workInProgressRootRenderPhaseUpdatedLanes = mergeLanes(
workInProgressRootRenderPhaseUpdatedLanes,
lane,
);
} else {
if (enableUpdaterTracking) {
if (isDevToolsPresent) {
addFiberToLanesMap(root, fiber, lane);
}
}
}
第三步:处理中间更新(Interleaved Updates)
js
if (root === workInProgressRoot) {
if (
deferRenderPhaseUpdateToNextBatch ||
(executionContext & RenderContext) === NoContext
) {
workInProgressRootInterleavedUpdatedLanes = mergeLanes(
workInProgressRootInterleavedUpdatedLanes,
lane,
);
}
if (workInProgressRootExitStatus === RootSuspendedWithDelay) {
markRootSuspended(root, workInProgressRootRenderLanes);
}
}
第四步:核心调度 - ensureRootIsScheduled
js
ensureRootIsScheduled(root, eventTime);
console.log("✅ ~ ensureRootIsScheduled:", ensureRootIsScheduled)
-
ensureRootIsScheduled是整个函数的 核心。 -
它负责:
-
确定下一个要处理的优先级(
getNextLanes) -
检查是否可以重用现有任务
-
根据优先级选择调度策略:
- 同步更新 →
scheduleSyncCallback/scheduleLegacySyncCallback - 并发更新 →
scheduleCallback并传入调度器优先级
- 同步更新 →
-
第五步:处理同步更新的特殊情况
js
if (
lane === SyncLane &&
executionContext === NoContext &&
(fiber.mode & ConcurrentMode) === NoMode &&
!(__DEV__ && ReactCurrentActQueue.isBatchingLegacy)
) {
resetRenderTimer();
flushSyncCallbacksOnlyInLegacyMode();
}
ensureRootIsScheduled 的核心逻辑
同步调度
js
if (newCallbackPriority === SyncLane) {
if (root.tag === LegacyRoot) {
scheduleLegacySyncCallback(performSyncWorkOnRoot.bind(null, root));
} else {
scheduleSyncCallback(performSyncWorkOnRoot.bind(null, root));
}
}
并发调度
js
else {
let schedulerPriorityLevel;
switch (lanesToEventPriority(nextLanes)) {
case DiscreteEventPriority:
schedulerPriorityLevel = ImmediateSchedulerPriority;
break;
case ContinuousEventPriority:
schedulerPriorityLevel = UserBlockingSchedulerPriority;
break;
case DefaultEventPriority:
schedulerPriorityLevel = NormalSchedulerPriority;
break;
case IdleEventPriority:
schedulerPriorityLevel = IdleSchedulerPriority;
break;
default:
schedulerPriorityLevel = NormalSchedulerPriority;
break;
}
newCallbackNode = scheduleCallback(
schedulerPriorityLevel,
performConcurrentWorkOnRoot.bind(null, root),
);
}
总结
scheduleUpdateOnFiber 的核心作用:
-
更新调度入口:将 Fiber 更新转换为可调度任务
-
优先级管理:根据 Lane 确定更新优先级
-
调度策略选择:
- 同步更新 →
performSyncWorkOnRoot - 并发更新 →
performConcurrentWorkOnRoot
- 同步更新 →
-
任务队列管理:将任务添加到相应调度队列
-
中断与恢复:处理渲染过程中的新更新
整体流程:
js
scheduleUpdateOnFiber
↓
ensureRootIsScheduled
↓
优先级判断 → 同步/并发调度
↓
performSyncWorkOnRoot / performConcurrentWorkOnRoot
↓
实际的渲染工作循环
首次 root.render 注册同步任务
首次调用:
js
root.render(<App />);
流程概览
- React 创建 根 Fiber 对应
<App />。 - React 创建 更新对象(Update) 。
- 调用
enqueueUpdate(fiber, update)将更新加入 Fiber 更新队列。 - 调用:
js
scheduleUpdateOnFiber(root, root.current, SyncLane, eventTime);
首次渲染使用 SyncLane(同步更新) 。
scheduleUpdateOnFiber 注册任务的逻辑
- 获取下一个要执行的 Lane:
js
const nextLanes = getNextLanes(root); // 首次渲染只有 SyncLane
- 判断同步调度:
js
if (newCallbackPriority === SyncLane) {
if (root.tag === LegacyRoot) {
scheduleLegacySyncCallback(performSyncWorkOnRoot.bind(null, root));
} else {
scheduleSyncCallback(performSyncWorkOnRoot.bind(null, root));
}
}
- ConcurrentRoot(React18+) →
scheduleSyncCallback(performSyncWorkOnRoot(root)) - LegacyRoot(React17 及以前) →
scheduleLegacySyncCallback(performSyncWorkOnRoot(root))
✅ 首次渲染注册的是 同步任务 ,直接执行 performSyncWorkOnRoot,构建 Fiber 树并提交 DOM。
任务类型总结
| 场景 | Root 类型 | Lane | 注册任务函数 | 调度方式 |
|---|---|---|---|---|
| 首次 render / React18+ | ConcurrentRoot | SyncLane | performSyncWorkOnRoot(root) |
同步调度 |
| 首次 render / React17 | LegacyRoot | SyncLane | performSyncWorkOnRoot(root) |
同步队列 |
| 用户事件触发更新 | ConcurrentRoot | 非SyncLane | performConcurrentWorkOnRoot(root) |
并发调度 |
执行顺序
js
root.render(<App />)
↓
enqueueUpdate(fiber, update)
↓
scheduleUpdateOnFiber(root, fiber, SyncLane)
↓
ensureRootIsScheduled
↓
scheduleSyncCallback(performSyncWorkOnRoot(root))
↓
执行 performSyncWorkOnRoot → 构建 Fiber 树 → 提交 DOM
⚡ 首次
root.render不会进入并发队列,它是一个 同步任务,保证页面立即渲染。
小结
scheduleUpdateOnFiber是 React 从 更新到调度任务 的核心函数。- 首次
root.render使用 同步 Lane ,注册performSyncWorkOnRoot同步任务。 - 同步任务保证首次渲染立即完成,而并发更新机制从第二次更新开始发挥作用。
- 了解首次渲染任务机制,有助于理解 React 并发模式与同步渲染的差异。