【react18原理探究实践】调度机制之注册任务

上一篇我们讲到了,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 是整个函数的 核心

  • 它负责:

    1. 确定下一个要处理的优先级(getNextLanes

    2. 检查是否可以重用现有任务

    3. 根据优先级选择调度策略:

      • 同步更新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 的核心作用

  1. 更新调度入口:将 Fiber 更新转换为可调度任务

  2. 优先级管理:根据 Lane 确定更新优先级

  3. 调度策略选择:

    • 同步更新 → performSyncWorkOnRoot
    • 并发更新 → performConcurrentWorkOnRoot
  4. 任务队列管理:将任务添加到相应调度队列

  5. 中断与恢复:处理渲染过程中的新更新

整体流程

js 复制代码
scheduleUpdateOnFiber
        ↓
   ensureRootIsScheduled
        ↓
优先级判断 → 同步/并发调度
        ↓
performSyncWorkOnRoot / performConcurrentWorkOnRoot
        ↓
实际的渲染工作循环

首次 root.render 注册同步任务

首次调用:

js 复制代码
root.render(<App />);

流程概览

  1. React 创建 根 Fiber 对应 <App />
  2. React 创建 更新对象(Update)
  3. 调用 enqueueUpdate(fiber, update) 将更新加入 Fiber 更新队列。
  4. 调用:
js 复制代码
scheduleUpdateOnFiber(root, root.current, SyncLane, eventTime);

首次渲染使用 SyncLane(同步更新)


scheduleUpdateOnFiber 注册任务的逻辑

  1. 获取下一个要执行的 Lane:
js 复制代码
const nextLanes = getNextLanes(root); // 首次渲染只有 SyncLane
  1. 判断同步调度:
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 并发模式与同步渲染的差异
相关推荐
GISer_Jing2 分钟前
前端营销技术实战:数据+AI实战指南
前端·javascript·人工智能
GIS之路21 分钟前
使用命令行工具 ogr2ogr 将 CSV 转换为 Shp 数据(二)
前端
嘉琪00134 分钟前
Vue3+JS 高级前端面试题
开发语言·前端·javascript
vipbic1 小时前
用 Turborepo 打造 Strapi 插件开发的极速全栈体验
前端·javascript
天涯学馆1 小时前
为什么 JavaScript 可以单线程却能处理异步?
前端·javascript
Henry_Lau6172 小时前
主流IDE常用快捷键对照
前端·css·ide
陶甜也2 小时前
使用Blender进行现代建筑3D建模:前端开发者的跨界探索
前端·3d·blender
我命由我123452 小时前
VSCode - Prettier 配置格式化的单行长度
开发语言·前端·ide·vscode·前端框架·编辑器·学习方法
HashTang2 小时前
【AI 编程实战】第 4 篇:一次完美 vs 五轮对话 - UnoCSS 配置的正确姿势
前端·uni-app·ai编程
JIngJaneIL2 小时前
基于java + vue校园快递物流管理系统(源码+数据库+文档)
java·开发语言·前端·数据库·vue.js