react源码学习第 5 周:调度系统(Scheduler)

🎯 目标:

  • 理解 React 是怎么实现异步渲染

  • 掌握优先级调度的核心机制

📂 推荐文件:

  • packages/scheduler/src/Scheduler.js

  • ReactFiberWorkLoop.new.js 中的调度调用

🔍 调试建议:

  • 打印不同优先级任务的执行时间

  • 模拟阻塞型组件对 scheduler 的影响

理解 React 是怎么实现异步渲染 

React 实现异步渲染的关键在于其 Fiber 架构,这是一种重写后的协调引擎(React 16 引入),目的是使 React 能够 中断、恢复和分段执行渲染任务,从而提升响应性能。

下面是逐步理解 React 异步渲染的核心机制:

1. 为什么需要异步渲染?

在 React 15 及以前,渲染是同步的、递归的,如果一个组件树很深,更新一次可能会卡住主线程,导致 UI 卡顿、输入延迟,特别是动画、输入等高优先级任务会被阻塞。

2. Fiber 架构核心:可中断的任务模型

Fiber 是什么?

Fiber 是 React 为了解决可中断渲染问题而引入的数据结构,每一个 Fiber 节点表示组件树中的一个单元,它允许任务在执行中被中断和恢复。

它的两个特性:

  • 可以中断(yield)执行,放弃主线程控制权。

  • 能够在后续继续上次中断的地方(保持状态)。

3. 异步渲染流程简化版

React 渲染大致流程分为两阶段:

✅ Render Phase(可中断)

  • 构建 Fiber 树,更新虚拟 DOM。

  • 调用组件函数、生成新 Fiber。

  • 使用 requestIdleCallback / scheduler 等方式分片执行

  • 每执行一个 fiber 单元,React 检查是否需要中断(例如用户点击了按钮)。

function workLoop(deadline) {
  while (nextUnitOfWork && deadline.timeRemaining() > 1) {
    nextUnitOfWork = performUnitOfWork(nextUnitOfWork);
  }

  if (nextUnitOfWork) {
    requestIdleCallback(workLoop);
  }
}

在 React 18 中,使用了更高级的调度器 scheduler,而非 requestIdleCallback。

Commit Phase(同步)

  • 把变化真实应用到 DOM(副作用执行,如 DOM 操作)。

  • 不能中断,必须一次性完成。

4. 调度优先级机制

React 实现异步渲染还依赖于任务调度机制(Scheduler),引入了多个优先级:

  • Immediate

  • User-blocking

  • Normal

  • Low

  • Idle

这样 React 能:

  • 高优任务(比如点击)优先执行。

  • 低优任务(比如列表更新)延后,避免抢占资源。

React 使用 Scheduler 对任务进行调度,将任务放入一个优先级队列中,并使用 MessageChannel 实现微任务调度。

 

5. Concurrent Mode(并发模式)

React 18 中引入的 Concurrent Features(并发特性) 是异步渲染能力的正式体现:

  • 支持自动中断 & 恢复渲染。

  • 使用 startTransition 显式标记“可打断任务”。

  • 支持 SuspenseuseDeferredValue 等延迟加载体验。

示例:

import { startTransition } from 'react';

const handleInput = (e) => {
  const value = e.target.value;
  // 高优操作:设置输入框值
  setInputValue(value);

  // 低优操作:搜索筛选
  startTransition(() => {
    setFilteredList(filterBigList(value));
  });
};

 React 异步渲染是通过 Fiber 架构将渲染过程拆分为可中断的工作单元,并结合 Scheduler 的任务调度,实现了任务优先级控制与渲染过程打断恢复,从而提供了更流畅的用户体验。

 掌握优先级调度的核心机制

一、理解调度的背景和目标

✅ 为什么要有调度系统?

React 的调度系统不是为了「让任务更快」,而是为了合理安排执行顺序,保证:

  • 用户操作优先响应(如输入、点击)

  • 后台任务低优执行(如懒加载列表)

  • 渲染任务可中断、中止、重试

React 为此内置了 Scheduler 调度器,用于在浏览器主线程中协调多个任务的执行优先级

二、理解调度优先级模型(调度器核心 API)

React 中的优先级主要在 react-reconcilerscheduler 这两个包中体现。

优先级等级(五种):

React 中的调度优先级是枚举型数字越小优先级越高:

优先级示例场景
ImmediatePriority1useEffect 清理
UserBlockingPriority2输入响应
NormalPriority3普通更新(如 setState)
LowPriority4日志打点,懒加载等
IdlePriority5浏览器空闲处理任务

Scheduler 核心 API:

import { unstable_scheduleCallback, unstable_NormalPriority } from 'scheduler';

unstable_scheduleCallback(unstable_NormalPriority, () => {
  console.log('任务执行');
});

 任务会根据优先级被插入一个最小堆中(基于 expirationTime 进行排序)。

三、掌握 React 中调度的实际触发点(Fiber 层对接 Scheduler)

Fiber 更新任务中会创建一个更新对象:

{
  lane: SyncLane | DefaultLane | TransitionLane | ...,
  tag: UpdateState,
  payload: newState,
}

React 会将每个更新任务标记上一个 Lane(车道),这本质上是一个位运算的优先级系统

Lane 类型描述对应调度优先级
SyncLane同步任务ImmediatePriority
InputLane用户输入响应任务UserBlockingPriority
DefaultLane默认任务NormalPriority
TransitionLaneUI 渲染过渡任务LowPriority(startTransition
IdleLane闲时任务IdlePriority

 

四、调度是如何执行的?(任务执行模型)

  1. 调度任务 -> 插入优先队列(小顶堆)

  2. 每一帧渲染时,React 会从队列中取出到期任务(根据当前时间和过期时间)

  3. 如果任务不能在一帧时间(16ms)内完成,会中断执行(yield),下一帧继续

调度中断判断方式(简化):

if (shouldYield()) {
  // 中断当前任务,等待下一帧继续
  scheduleCallback(priority, continueTask);
}

 shouldYield() 会判断是否接近帧尾(使用 performance.now()MessageChannel 实现)

五、实践调度优先级的几种方式

1️⃣ 使用 startTransition 降低任务优先级

import { startTransition } from 'react';

startTransition(() => {
  setState(); // 被标记为 TransitionLane
});

 2️⃣ 使用 useDeferredValue 控制数据延迟渲染

const deferredValue = useDeferredValue(inputValue); // 降低优先级

 3️⃣ 使用 unstable_scheduleCallback 创建自定义调度

unstable_scheduleCallback(unstable_LowPriority, () => {
  console.log('后台任务');
});

六、源码学习路径建议

你可以阅读以下源码文件(按重要程度排序):

  1. scheduler package

  2. react-reconciler

  3. Lane 模型

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值