🎯 目标:
-
理解 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显式标记“可打断任务”。 -
支持
Suspense和useDeferredValue等延迟加载体验。
示例:
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-reconciler 和 scheduler 这两个包中体现。
优先级等级(五种):
React 中的调度优先级是枚举型数字越小优先级越高:
| 优先级 | 值 | 示例场景 |
|---|---|---|
| ImmediatePriority | 1 | useEffect 清理 |
| UserBlockingPriority | 2 | 输入响应 |
| NormalPriority | 3 | 普通更新(如 setState) |
| LowPriority | 4 | 日志打点,懒加载等 |
| IdlePriority | 5 | 浏览器空闲处理任务 |
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 |
| TransitionLane | UI 渲染过渡任务 | LowPriority(startTransition) |
| IdleLane | 闲时任务 | IdlePriority |
四、调度是如何执行的?(任务执行模型)
-
调度任务 -> 插入优先队列(小顶堆)
-
每一帧渲染时,React 会从队列中取出到期任务(根据当前时间和过期时间)
-
如果任务不能在一帧时间(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('后台任务');
});
六、源码学习路径建议
你可以阅读以下源码文件(按重要程度排序):
-
scheduler package:
-
实现了任务调度、优先队列、时间控制
-
react-reconciler:
-
这是执行 Fiber 渲染和提交的主流程
-
Lane 模型:
-
理解 Lane 位运算是深入理解优先级机制的关键
&spm=1001.2101.3001.5002&articleId=148843417&d=1&t=3&u=8871df6f38e4477386bdc06fbec99f7b)
338

被折叠的 条评论
为什么被折叠?



