一. React Fiber 的核心目标
- 增量渲染:将大型更新拆解为可中断的5ms任务块(时间切片),避免阻塞
示例:长列表渲染时,优先处理可视区域内容。
-
主线程优先级调度:动态管理任务执行顺序(如用户交互 > 动画 > 数据加载)。
// 优先级等级
const PriorityLevels = {
Immediate: 99, // 用户点击
UserBlocking: 98, // 动画
Normal: 97 // 数据加载
}; -
与浏览器协作:利用浏览器渲染周期(rAF)和空闲时间(rIC理念)优化性能。
1.高优先级任务用 requestAnimationFrame(对齐屏幕刷新率)
2.低优先级任务用 requestIdleCallback(利用空闲时间)
一句话定义:
React Fiber是React 16+的核心重写算法,通过增量渲染和任务分片实现高性能异步更新。旨在解决传统同步渲染阻塞主线程导致的卡顿问题。
二. React Fiber 的核心原理架构
2.1 数据结构变革
- 递归同步 → 链表异步(Fiber节点)
- 双缓冲机制(current/workInProgress树交替)
之前的版本中,React使用递归
的方式处理组件树的更新,这个过程是同步
的,一旦开始就不能中断,可能导致长时间占用主线程,造成界面卡顿,尤其是在处理复杂组件时。
Fiber是将组件树
拆解为链表结构
的 Fiber 节点
(每个节点对应一个可中断/恢复
的"工作单元"),通过优先级调度
(如用户交互任务优先)和时间切片
(利用浏览器空闲时间分批次处理任务),实现异步可中断
的渲染流程。
Fiber 在协调阶段
(Render Phase)增量构建虚拟树
并标记副作用(如 DOM 更新),在提交阶段
(Commit Phase)同步
执行所有变更,同时通过双缓冲机制
(交替使用 current 和 workInProgress 树)和副作用链表优化性能,从而支持并发模式(Concurrent Mode),显著提升复杂应用的响应速度与流畅度。
在 React 的 Fiber 架构中,requestAnimationFrame
、链表
和 调度器
三者紧密结合,主要体现在 任务调度策略
和 执行效率优化
上
2.1. Fiber的双链表系统
Fiber 树本身的链表结构和副作用链表(effect list)是两个不同的链表
js
class FiberNode {
child: Fiber | null; // 子节点
sibling: Fiber | null; // 兄弟节点
return: Fiber | null; // 父节点
firstEffect: Fiber | null;// 副作用链表头
}
2.1.1. Fiber 树链表(主链表)
作用:
表示
组件树
的完整结构
,用于协调阶段(reconciliation)
的遍历
和任务分片
。
数据结构:
每个 Fiber 节点通过
child、sibling、return
三个指针构成树形链表(深度优先遍历的线性化结构)。
js
class FiberNode {
child: Fiber | null; // 第一个子节点
sibling: Fiber | null; // 下一个兄弟节点
return: Fiber | null; // 父节点
// ... 其他属性(type, stateNode 等)
}
特点:
1.用于
递归的可中断恢复
(通过保存当前处理的 Fiber 节点指针)。2.是 React
处理组件更新
的核心数据结构。
与调度器的关系
:
每个链表节点(FiberNode)包含任务信息
、优先级标记
和指针
(child/sibling/return)
调度器通过链表实现可中断恢复
:保存当前节点指针,下次从中断处继续
2.1.2. 副作用链表(effect list)
作用:
收集所有需要
执行副作用的 Fiber 节点
(如 DOM 操作、生命周期调用),在提交阶段
(commit phase)批量处理
。
数据结构:
通过
firstEffect(链表头)、lastEffect(链表尾)、nextEffect(每个节点的指针)
指针构成的单向链表。
JS
class FiberNode {
firstEffect: Fiber | null; // 链表头
lastEffect: Fiber | null; // 链表尾
nextEffect: Fiber | null; // 下一个副作用节点
effectTag: Placement | Update | Deletion; // 副作用类型
}
关键特性:
1.高效批处理
1.是
Fiber 树的子集
,仅包含需要处理
的节点。2.在
协调阶段
动态构建,提交阶段
直接遍历此链表批量处理 DOM 操作(如DOM更新
、生命周期方法
)
2.优先级处理
1.调度器控制
构建顺序
,高优先级
任务优先生成链表
(如用户输入触发的更新可能打断正在进行的渲染,优先处理相关副作用,然后再处理低优先级的任务)2.支持
中断恢复
,保留未完成的链表状态
3.执行分类和时机
同步副作用(useLayoutEffect):
执行时机:
DOM更新后,浏览器绘制前
特点:必须
立即完成
的工作,会阻塞浏览器渲染
类比:就像手术中的紧急止血,必须马上处理
典型场景:调整元素位置、获取DOM尺寸、必须同步执行的动画效果
异步副作用(useEffect):执行时机:浏览器
完成绘制后
的空闲时间
特点:可以
稍后完成
的工作,不阻塞渲染
- 类比:就像整理病历档案,可以等主要工作完成后处理
典型场景:API请求、事件订阅、数据获取、事件监听、非关键的初始化操作
js
function Example() {
// 同步副作用(紧急)
useLayoutEffect(() => {
const rect = element.getBoundingClientRect();
console.log('元素尺寸:', rect);
});
// 异步副作用(非紧急)
useEffect(() => {
fetch('/api/data').then(/*...*/);
});
}
4.遍历顺序
采用深度优先后序遍历,确保:
子节点
副作用先于父节点
处理- DOM操作符合依赖关系(如先删除子节点再更新父节点)
为什么分开设计?
1.关注点分离:
Fiber 树链表负责
任务调度和中断恢复
(协调阶段)。副作用链表负责
高效执行 DOM 操作
(提交阶段)。
2.性能优化:
提交阶段只需遍历
少量副作用节点
,避免全树遍历。
批量处理
DOM 更新,减少浏览器重排/重绘。
总结
:
不是同一个链表,但副作用链表是 Fiber 树的衍生结构
。
Fiber 树是全局任务管理
的骨架,副作用链表是局部优化
的结果。
两者协作实现 React 的可中断渲染和高效更新
。

2.2. requestAnimationFrame(rAF):调度时机控制
requestAnimationFrame(rAF) 是浏览器提供的原生 API,用于在下一次屏幕刷新
(通常是每秒 60 次,即 16.6ms/帧)前执行回调函数。
作用
在每一帧
渲染前
执行高优先级任务
(如动画、UI 更新)
与调度器的关系:
1.动画优先级任务:动画、布局更新等高优先级任务,通过 rAF 对齐浏览器刷新率(通常 60fps/16.6ms)
js
// 伪代码:调度器中的优先级处理
if (task.priority === UserBlockingPriority) {
requestAnimationFrame(() => performTask(task));
}
2.避免布局抖动:在 rAF 回调中批量处理 DOM 读写,确保 DOM 操作在
浏览器渲染周期内
执行,减少强制布局计算(如 offsetWidth)。
特性 | requestAnimationFrame | requestIdleCallback |
---|---|---|
用途 | 处理高优先级任务(如动画、用户交互) | 处理低优先级任务(如数据加载) |
Fiber 中的应用场景 | 同步任务队列 | 异步任务队列(后备 polyfill) |
触发时机 | 每帧开始前(16.7ms/60FPS) | 浏览器空闲时(无高优先级任务) |
用途 | 动画、视觉更新 | 后台任务(日志、预加载) |
优先级 | 高(与渲染强相关) | 低(可被高优先级任务打断) |
React调度参考 | 影响高优先级任务分片 | 启发低优先级任务分片(如并发渲染) |
执行耗时限制 | 需控制在3-4ms以内 | 默认50ms(通过timeout 参数可调) |
兼容性 | IE10+ | 需polyfill(如React的scheduler ) |
2.3. React Fiber 调度机制(Scheduler)
是其并发模式(Concurrent Mode)的核心底层机制,相比传统更新调度方式具有以下显著优势:
2.3.1.优先级调度系统
更精细的任务优先级控制
- 5级优先级划分机制:
协作流程示例
- 用户点击按钮(Immediate 优先级):
同步执行回调,更新状态,标记相关 Fiber 节点。- 触发动画or搜索建议(UserBlocking 优先级):
将更新任务放入 rAF 队列,确保下一帧渲染前完成。- 数据加载(Low 优先级):
拆分任务为 5ms 的块,在调度器检测到空闲时逐步执行。
js
// 代码示例:优先级实战判断
startTransition(() => {
// ↓ Low优先级(数据加载)
setData(fetchData());
});
button.onclick = () => {
// ↑ Immediate优先级(用户交互)
setState(...);
};
- 动态调整机制:高优先级任务可中断低优先级任务(如渲染中途响应用户点击)。
- 中断/恢复机(保存当前Fiber指针)
2.3.2.执行控制机制
2.3.2.1. 时间切片(Time Slicing)机制
时间切片逻辑:将任务
拆分
为5ms
的块,动态检查空闲时间
,通过shouldYield()判断是否让出主线程,保证UI响应。优先处理用户输入
等高优先级
事件,避免阻塞用户交互。
这听起来类似于浏览器的requestIdleCallback API
,允许在空闲时间执行任务,但React实现了自己的调度器。
requestIdleCallback(rIC)的理念
空闲时段处理
低优先级
任务:rIC 在浏览器空闲时执行非紧急任务
(如日志、预加载),避免干扰关键渲染和交互。
『时间分片』作为执行策略(设计原则)的核心概念
- 是什么:是Fiber架构的核心设计思想,将渲染工作分解为小任务块
- 为什么:保证浏览器不卡顿(主线程可响应事件)
- 特征:不关心具体如何实现分片,策略决定需要切片
2.3.2.2. 空闲期利用:
通过requestIdleCallback
的polyfill(兼容方案),在浏览器空闲时执行低优先级任务(如日志上报、预渲染)。
2.3.2.3 任务饥饿保护:
防止低优先级任务因长期无法执行被"饿死"(超过超时时间会强制提升优先级)。
js
// 伪代码实现
if (task.expirationTime < currentTime) {
task.priority = Immediate; // 强制升级
}
2.3.3 调度机制实现方案
2.3.3.1. 基于MessageChannel的调度:React 自研调度器
浏览器兼容处理
1.降级策略:
- 首选:MessageChannel(精度最高)
- 次选:setImmediate(IE10+)
兜底
:setTimeout(所有浏览器)
2.关键原因:
- MessageChannel的
postMessage
能实现微秒级调度- 比setTimeout更精准(不受
浏览器最小延迟
限制)
2.3.3.2. 性能优化策略(3大核心)
1.任务批处理:
- 合并多个setState更新
示例:连续3次setState只会触发1次渲染
2.优先级缓存:
- 记住组件树的
优先级标记
- 跳过
未变化子树
的调度(bailout机制)
3.动态时间切片:
- 基础值5ms,但会
根据设备性能调整
- 高性能设备可能用2ms,低端设备可能用10ms
为什么选择5ms分片?
- 5ms是
启发式值
(heuristic),实际会根据设备性能动态调整- 人眼最小感知延迟约16ms(60fps)
- 5ms分片确保至少剩余11ms处理用户输入
- 平衡任务进度和响应速度的黄金值
『时间分片』作为实现方案(技术手段)的概念
- 怎么做:通过MessageChannel/postMessage控制执行时长
- 关键代码:实际测量任务执行时间的while循环
- 特征:依赖具体API和算法实现的核心概念,实现确保能正确切片`
2.3.4. 对比传统调度方案
能力 | 传统setState更新 | React调度器 |
---|---|---|
任务中断 | ✗ 不可中断 | ✔ 高优任务可中断低优任务 |
浏览器阻塞 | 可能阻塞主线程 | 通过时间切片避免阻塞 |
优先级控制 | 无差别处理 | 5级动态优先级 |
后台任务利用 | 依赖手动实现 | 内置空闲期调度 |
2.4 双缓冲机制
2.4.1. 双缓冲的定义
Fiber 架构的核心机制,用于在
协调阶段
(Reconciliation) 和 提交阶段
(Commit) 之间高效管理组件状态更新
,确保 React 的 可中断渲染
和 一致性更新
。
React 在 Fiber 架构中维护 两棵 Fiber 树:
-
current
树:当前已渲染的 Fiber 树(对应真实 DOM
,对应屏幕上显示的内容)。 -
workInProgress
树:正在构建的新 Fiber 树
(用于计算更新,尚未提交到屏幕)。
2.4.2. 双缓冲与调度的关系:
- 调度器决定"
何时执行
"- 双缓冲解决"
如何安全执行
"
2.4.3. 双缓冲的核心流程
(1) 协调阶段(Reconciliation)
- React 从 current 树
克隆出
workInProgress 树(通过 alternate 指针关联)。 - 在
workInProgress
树上进行 Diff 计算
,标记需要更新的节点
(effectTag)。 - 如果任务被中断(如高优先级任务插入),可以
丢弃 workInProgress 树并重新开始
,而不会影响 current 树(已渲染的 UI)。
(2) 提交阶段(Commit)
当 workInProgress 树构建完成,React 执行 原子性切换:
- 新的 workInProgress 树变为 current 树(对应最新 UI)。
- 旧的 current 树变为新的 workInProgress 树(供下次更新使用)。
这段如果不是很好理解,D老师给了例子哈哈:
想象您有两块画板:
展示画板(current树)- 观众正在看的
备用画板(workInProgress树)- 您正在修改的
更新流程:1.当需要修改时,您会复制展示画板到备用画板(克隆)
2.在备用画板上进行修改(添加/删除元素)
3.修改完成后瞬间交换两块画板的位置
4.原来的展示画板变成新的备用画板(供下次修改使用)
用我的话形容就是:"两个东西来回倒腾,每次改完都把最新的甩到前台给人看,用完的旧货也不扔,拉回去重新刷漆等着下次用
"
🔍 需要强调的细节:
- 交换是"
瞬间完成
"的(像电灯开关,没有中间状态
) 旧树会被复用
js
// 伪代码
// 协调阶段:构建更新
function beginWork() {
const workInProgressFiberTree = cloneFiberTree(currentFiberTree);
performUnitOfWork(workInProgressFiberTree); // 增量更新
if (shouldYieldToBrowser()) {
return; // 可中断
}
commitRoot(); // 完成更新
}
// 提交阶段:完成更新切换
function commitRoot() {
currentFiberTree = workInProgressFiberTree; // 新树变为当前树
workInProgressFiberTree = null; // 清空工作树
}
通过维护两棵 Fiber 树(current
和 workInProgress
),实现:
- 计算与渲染分离。
- 高优先级任务抢占。
- 无闪烁的 UI 更新。
最终提升复杂应用的流畅度和响应速度。
双缓存机制 vs 调度系统
三、总结
同步渲染与react fiber的对比