【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 并发模式与同步渲染的差异
相关推荐
前端 贾公子几秒前
从入门到实践:前端 Monorepo 工程化实战(4)
前端
菩提小狗3 分钟前
Sqlmap双击运行脚本,双击直接打开。
前端·笔记·安全·web安全
前端工作日常14 分钟前
我学习到的AG-UI的概念
前端
韩师傅20 分钟前
前端开发消亡史:AI也无法掩盖没有设计创造力的真相
前端·人工智能·后端
XiaoYu200234 分钟前
第12章 支付宝SDK
前端
双向331 小时前
RAG的下一站:检索增强生成如何重塑企业知识中枢?
前端
拖拉斯旋风1 小时前
从零开始:使用 Ollama 在本地部署开源大模型并集成到 React 应用
前端·javascript·ollama
栀秋6661 小时前
智能驱动的 Git 提交:基于 Ollama 大模型的规范化提交信息生成方案
react.js·llm·ollama
asing1 小时前
🤯 为什么我的收银台在鸿蒙系统“第一次返回”死活拦不住?一次差点背锅的排查实录
前端·harmonyos
德育处主任1 小时前
『NAS』在群晖部署图片压缩工具-Squoosh
前端·javascript·docker