React Fiber 的优先级系统是它支持异步渲染、任务调度和并发特性的核心机制之一。这个系统允许 React 区分不同类型的更新任务,按优先级顺序执行,从而实现:
- ✅ 用户交互响应优先
- ✅ 渲染任务可以被打断
- ✅ 更流畅的动画和输入体验
🧠 一、为什么需要优先级系统?
传统的 React 15 或 Vue 2 中,所有更新都是同步更新:一旦开始就必须执行完,哪怕中间来了更紧急的任务(比如用户点击、输入),也得等当前渲染结束才能响应。
这就可能导致:
- 页面卡顿
- 输入延迟
- 动画掉帧
为了解决这个问题,React Fiber 将更新拆成小任务,并引入"优先级"系统来决定哪些任务先执行、哪些可以延后或打断重来。
🛣️ 二、Fiber 优先级系统的发展历程
React 16 之前:使用 expiration time(过期时间)
- 用时间戳表示优先级
- 越小越紧急
- 存在调度不够灵活的问题
React 17 之后:引入 Lane 模型(车道系统)
- 每一个更新被分配一个 Lane
- Lane 是二进制标识位(bitmask)
- 可组合(多种更新类型可以合并执行)
- 支持并发任务管理
🧩 三、Lane 模型详解
Lane 是一个 31 位的二进制位,每一位代表一种优先级。
常见 Lane 类型
Lane 名称 | 示例 | 说明 | 优先级 |
---|---|---|---|
SyncLane |
同步事件 | onClick 、setState |
🟥 最高 |
InputContinuousLane |
滚动、拖拽 | 滑动条、拖拽 | 🟧 很高 |
DefaultLane |
普通更新 | 状态更新、数据变化 | 🟨 中等 |
TransitionLane |
过渡更新 | startTransition |
🟦 可打断 |
IdleLane |
闲时执行 | 日志、后台任务 | 🟩 最低 |
一个更新可以包含多个 Lane,比如同时是 Transition 和 Default,可以合并调度。
🔀 四、优先级调度的执行过程
1. 触发更新时选择 Lane
scss
setState(newValue);
// → assign lane: DefaultLane or SyncLane
由调度器根据上下文(事件、transition)为更新分配合适的 Lane。
2. 根节点调度表(PendingLanes)
每棵 Fiber 树的 root 上维护一个 pendingLanes
记录哪些 Lane 上有未处理的更新。
ini
root.pendingLanes = SyncLane | TransitionLane;
3. 调度器根据优先级挑选任务
React 使用自己的调度器包 scheduler
,模拟浏览器空闲时间。
调度器根据 Lane → 对应的 Scheduler 优先级:
Lane | Scheduler 优先级 |
---|---|
SyncLane | ImmediatePriority |
InputContinuousLane | UserBlockingPriority |
DefaultLane | NormalPriority |
TransitionLane | LowPriority |
IdleLane | IdlePriority |
🔂 五、优先级系统与中断机制的协同
如何中断低优先级任务?
在 workLoopConcurrent
中,每处理一个单元后会调用:
scss
shouldYield()
用于判断是否该中断当前任务,让高优先级任务插队。
less
if (shouldYield()) {
// 暂停当前任务,下次继续
}
这种机制是 Fiber 调度的核心:时间分片 + 优先级抢占
🧪 六、startTransition 的本质
scss
startTransition(() => {
setState(slowData);
});
startTransition
会将内部更新标记为TransitionLane
- React 可以在空闲时执行,不会打断用户输入
- 用户输入如点击、打字触发的 SyncLane 会插队并立即执行
场景示例:
scss
<input onChange={(e) => {
setInput(e.target.value); // SyncLane
startTransition(() => {
setFilteredList(slowFilter()); // TransitionLane
});
}} />
用户输入不会卡顿,筛选操作可以延后一点点再更新。
🔍 七、React 是怎么判断哪个 Lane 要先执行?
React 会在根节点计算:
scss
getNextLanes(root)
按优先级从高到低取出下一个要处理的 Lane:
kotlin
if (SyncLane in pending) return SyncLane;
else if (InputContinuousLane in pending) return InputContinuousLane;
else if (DefaultLane in pending) return DefaultLane;
...
然后只构建对应 Lane 的 Fiber 子树,提高性能。
🧠 八、总结:优先级系统的核心价值
能力 | 描述 |
---|---|
任务抢占 | 低优先级任务可以被高优先级插队中断 |
时间分片 | 渲染被拆成小单元,每一帧可以只做一部分工作 |
多任务组合 | 多个任务可以用 Lane 合并执行 |
更好用户体验 | 实现不卡顿的输入、过渡和动画 |
📌 最后一张图理解优先级系统
php
setState → 分配 Lane(Sync / Default / Transition)
↓
Root Fiber.pendingLanes 标记该 Lane
↓
调度器选择最高优先级 Lane
↓
构建该 Lane 的 Fiber 子树
↓
每帧执行部分任务 → shouldYield → 中断继续下次