【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 并发模式与同步渲染的差异
相关推荐
hvang19888 小时前
某花顺隐藏了重仓涨幅,通过chrome插件计算基金的重仓涨幅
前端·javascript·chrome
Async Cipher8 小时前
TypeScript 的用法
前端·typescript
web打印社区8 小时前
vue页面打印:printjs实现与进阶方案推荐
前端·javascript·vue.js·electron·html
We་ct8 小时前
LeetCode 30. 串联所有单词的子串:从暴力到高效,滑动窗口优化详解
前端·算法·leetcode·typescript
木卫二号Coding8 小时前
Docker-构建自己的Web-Linux系统-Ubuntu:22.04
linux·前端·docker
CHU7290359 小时前
一番赏盲盒抽卡机小程序:解锁惊喜体验与社交乐趣的多元功能设计
前端·小程序·php
RFCEO9 小时前
前端编程 课程十二、:CSS 基础应用 Flex 布局
前端·css·flex 布局·css3原生自带的布局模块·flexible box·弹性盒布局·垂直居中困难
天若有情6739 小时前
XiangJsonCraft v1.2.0重大更新解读:本地配置优先+全量容错,JSON解耦开发体验再升级
前端·javascript·npm·json·xiangjsoncraft
2501_944525549 小时前
Flutter for OpenHarmony 个人理财管理App实战 - 预算详情页面
android·开发语言·前端·javascript·flutter·ecmascript
打小就很皮...10 小时前
《在 React/Vue 项目中引入 Supademo 实现交互式新手指引》
前端·supademo·新手指引