1. React Fiber 的核心目标
-
增量渲染:将大型更新拆解为可中断的小任务(时间切片),避免阻塞
-
主线程优先级调度:动态管理任务执行顺序(如用户交互 > 动画 > 数据加载)。
-
与浏览器协作:利用浏览器渲染周期(rAF)和空闲时间(rIC理念)优化性能。
一句话定义:
React Fiber是React 16+的核心重写算法,通过增量渲染和任务分片实现高性能异步更新。旨在解决传统同步渲染阻塞主线程导致的卡顿问题。
2. React Fiber 的核心原理
之前的版本中,React使用递归
的方式处理组件树的更新,这个过程是同步
的,一旦开始就不能中断,可能导致长时间占用主线程,造成界面卡顿,尤其是在处理复杂组件时。
Fiber是将组件树
拆解为链表结构
的 Fiber 节点
(每个节点对应一个可中断/恢复
的“工作单元”),通过优先级调度
(如用户交互任务优先)和时间切片
(利用浏览器空闲时间分批次处理任务),实现异步可中断
的渲染流程。
Fiber 在协调阶段
(Render Phase)增量构建虚拟树
并标记副作用(如 DOM 更新),在提交阶段
(Commit Phase)同步
执行所有变更,同时通过双缓冲机制
(交替使用 current 和 workInProgress 树)和副作用链表优化性能,从而支持并发模式(Concurrent Mode),显著提升复杂应用的响应速度与流畅度。
在 React 的 Fiber 架构中,requestAnimationFrame
、链表
和 调度器
三者紧密结合,主要体现在 任务调度策略
和 执行效率优化
上
2.1. Filber中的链表(任务存储与遍历)
Fiber 树本身的链表结构和副作用链表(effect list)是两个不同的链表
2.1.1. Fiber 树链表(主链表)
作用:
表示
组件树
的完整结构
,用于协调阶段(reconciliation)
的遍历
和任务分片
。
数据结构:
每个 Fiber 节点通过
child、sibling、return
三个指针构成树形链表(深度优先遍历的线性化结构)。
class FiberNode {child: Fiber | null; // 第一个子节点sibling: Fiber | null; // 下一个兄弟节点return: Fiber | null; // 父节点// ... 其他属性(type, stateNode 等)
}
特点:
用于
递归的可中断恢复
(通过保存当前处理的 Fiber 节点指针)。
是 React处理组件更新
的核心数据结构。
与调度器的关系
:
每个链表节点(FiberNode)包含任务信息
、优先级标记
和指针
(child/sibling/return)
调度器通过链表实现可中断恢复
:保存当前节点指针,下次从中断处继续
2.1.2. 副作用链表(effect list)
作用:
收集所有需要执行副作用的 Fiber 节点(如 DOM 操作、生命周期调用),在提交阶段(commit phase)批量处理。
数据结构:
通过 firstEffect、lastEffect、nextEffect 指针构成的单向链表。
class FiberNode {firstEffect: Fiber | null; // 链表头lastEffect: Fiber | null; // 链表尾nextEffect: Fiber | null; // 下一个副作用节点effectTag: Placement | Update | Deletion; // 副作用类型
}
特点:
是
Fiber 树的子集
,仅包含需要处理
的节点。
在协调阶段
动态构建,提交阶段直接遍历此链表执行 DOM 操作
副作用链表(effect list)的工作流程
每个Fiber节点在完成工作后,如果有副作用,会被添加到链表中。firstEffect和lastEffect用来指向链表的头和尾,nextEffect
是每个节点的指针。在提交阶段,React遍历这个链表,依次处理每个副作用
,如DOM更新
、生命周期方法
的调用等。这种结构使得React能够高效地批量处理副作用
,而不必在协调阶段频繁操作DOM,从而提高性能。
React Fiber 架构中任务调度与副作用处理的优先级机制:
于优先级,副作用链表可能并不直接管理优先级
,而是由调度器决定哪些Fiber节点需要先处理
。高优先级的更新会导致重新构建副作用链表,确保紧急的副作用先执行。例如,用户输入触发的更新可能打断正在进行的渲染,优先处理相关副作用,然后再处理低优先级的任务。
- 中断与恢复:高优先级任务可中断当前协调过程,保留未完成的副作用链表,后续恢复时继续构建。
- 多优先级链表:React 内部维护多个链表(如 pendingPassiveEffects),区分同步与异步(如 useEffect)副作用。
-
同步副作用:useLayoutEffect 在提交阶段同步执行。
-
异步副作用:useEffect 被推入异步队列,在浏览器空闲时处理。
-
在构建链表时,React可能采用深度优先的后序遍历方式
,确保子节点的副作用在父节点之前处理
,这样在DOM操作时可以正确应用变更。例如,当子节点需要被删除时,先处理子节点的删除,再处理父节点的更新,避免父节点更新后子节点不存在导致的错误。
最后,需要理解副作用链表在React整个渲染流程中的位置。协调阶段(render phase
)负责找出所有需要变更的节点
并构建
副作用链表,提交阶段(commit phase)
则遍历链表执行变更
。这种分离使得React可以异步处理渲染,提高响应能力。
2.1.3. 为什么分开设计?
1.关注点分离:
Fiber 树负责任务调度和中断恢复(协调阶段)。
副作用链表负责高效执行 DOM 操作(提交阶段)。
2.性能优化:
提交阶段只需遍历少量副作用节点,避免全树遍历。
批量处理 DOM 更新,减少浏览器重排/重绘。
总结
:
不是同一个链表,但副作用链表是 Fiber 树的衍生结构
。
Fiber 树是全局任务管理
的骨架,副作用链表是局部优化
的结果。
两者协作实现 React 的可中断渲染和高效更新
。
2.2. requestAnimationFrame(rAF):调度时机控制
requestAnimationFrame(rAF) 是浏览器提供的原生 API,用于在下一次屏幕刷新
(通常是每秒 60 次,即 16.6ms/帧)前执行回调函数。
作用
在每一帧
渲染前
执行高优先级任务
(如动画、UI 更新)
与调度器的关系:
React 调度器
用 rAF 作为高优先级任务
的触发时机
,确保视觉更新及时
避免 setTimeout 因时间不精确导致的丢帧问题
核心目标:
- 动画与视觉更新:
rAF 确保回调函数在浏览器下一帧渲染前执行,与屏幕刷新率(60Hz)同步,避免丢帧。
- React 中的应用:
高优先级任务对齐:React 将动画、布局更新等高优先级任务标记为与 rAF 同步执行。
避免布局抖动:在 rAF 回调中批量处理 DOM 读写,减少强制布局计算(如 offsetWidth)。
特性 | requestAnimationFrame | requestIdleCallback |
---|---|---|
触发时机 | 每帧开始前(16.7ms/60FPS) | 浏览器空闲时(无高优先级任务) |
用途 | 动画、视觉更新 | 后台任务(日志、预加载) |
优先级 | 高(与渲染强相关) | 低(可被高优先级任务打断) |
React调度参考 | 影响高优先级任务分片 | 启发低优先级任务分片(如并发渲染) |
执行耗时限制 | 需控制在3-4ms以内 | 默认50ms(通过timeout 参数可调) |
兼容性 | IE10+ | 需polyfill(如React的scheduler ) |
2.3. React 自研调度器(Scheduler)
是其并发模式(Concurrent Mode)的核心底层机制,相比传统更新调度方式具有以下显著优势:
2.3.1.优先级调度
更精细的任务优先级控制
- 5级优先级划分:
Immediate(同步) > UserBlocking(用户交互) > Normal(默认) > Low(数据加载) > Idle(空闲任务)
协作流程示例
- 用户点击按钮(Immediate 优先级):
同步执行回调,更新状态,标记相关 Fiber 节点。- 触发动画or搜索建议(UserBlocking 优先级):
将更新任务放入 rAF 队列,确保下一帧渲染前完成。- 数据加载(Low 优先级):
拆分任务为 5ms 的块,在调度器检测到空闲时逐步执行。
- 动态调整:高优先级任务可中断低优先级任务(如渲染中途响应用户点击)。
2.3.2.避免浏览器主线程阻塞
1.时间切片(Time Slicing)的概念
时间切片逻辑:将任务
拆分
为5ms
的块,动态检查剩余时间
,通过shouldYield()判断是否让出主线程,保证UI响应。优先处理用户输入
等高优先级
事件。
用于将渲染任务
分解成更小的单元,这样可以在浏览器的空闲时段
执行这些任务,避免阻塞用户交互。这听起来类似于浏览器的requestIdleCallback API
,允许在空闲时间执行任务,但React可能自己实现了更精细的控制。
requestIdleCallback(rIC)的理念
空闲时段处理
低优先级
任务:rIC 在浏览器空闲时执行非紧急任务
(如日志、预加载),避免干扰关键渲染和交互。
2.基于MessageChannel的调度:
React 自研调度器,通过
MessageChannel
或setTimeout 模拟空闲检测
。
MessageChannel相比setTimeout,利用事件循环的宏任务机制更精准控制执行时机,避免任务堆积导致卡顿。
React Fiber 未直接使用 rIC:这是因为rIC的浏览器兼容性
和触发频率
不够理想。React的调度器会将任务分成小的时间片,并在每个时间片结束时检查是否有更高优先级的任务需要处理,从而避免长时间阻塞主线程。
2.3.3. 更智能的后台任务处理
空闲期
利用:
通过requestIdleCallback
的polyfill(兼容方案),在浏览器空闲时执行低优先级任务(如日志上报、预渲染)。
任务饥饿保护:
防止低优先级任务因长期无法执行被“饿死”(超过超时时间会强制提升优先级)。
2.3.4. 与React深度集成
协调器(Reconciler)联动:
调度器知晓Fiber节点结构,可精准暂停/恢复渲染任务,实现可中断渲染。
并发特性基础:
支持startTransition、useDeferredValue等API的底层依赖。
对比传统调度方案
能力 | 传统setState更新 | React调度器 |
---|---|---|
任务中断 | ✗ 不可中断 | ✔ 高优任务可中断低优任务 |
浏览器阻塞 | 可能阻塞主线程 | 通过时间切片避免阻塞 |
优先级控制 | 无差别处理 | 5级动态优先级 |
后台任务利用 | 依赖手动实现 | 内置空闲期调度 |
2.4 双缓冲机制
2.4.1. 双缓冲的定义
Fiber 架构的核心机制,用于在 协调阶段(Reconciliation) 和 提交阶段(Commit) 之间高效
管理组件状态更新
,确保 React 的 可中断渲染
和 一致性更新
。
双缓冲是计算机图形学中的经典技术,指 同时维护两套数据结构:
- 当前缓冲(Current):正在渲染或已渲染的 UI 状态(对应屏幕上显示的内容)。
- 工作缓冲(WorkInProgress,WIP):正在计算的新状态(尚未提交到屏幕)。
React 借用这一概念,在 Fiber 架构中维护 两棵 Fiber 树:
-
current
树:当前已渲染的 Fiber 树(对应真实 DOM
)。 -
workInProgress
树:正在构建的新 Fiber 树
(用于计算更新)。
2.4.2. 双缓冲的核心流程
(1) 协调阶段(Reconciliation)
- React 从 current 树
克隆出
workInProgress 树(通过 alternate 指针关联)。 - 在
workInProgress
树上进行 Diff 计算
,标记需要更新的节点
(effectTag)。 - 如果任务被中断(如高优先级任务插入),可以
丢弃 workInProgress 树并重新开始
,而不会影响 current 树(已渲染的 UI)。
(2) 提交阶段(Commit)
当 workInProgress 树构建完成,React 执行 原子性切换:
root.current = finishedWork; // 切换 current 和 workInProgress
此时:
- 新的 workInProgress 树变为 current 树(对应最新 UI)。
- 旧的 current 树变为新的 workInProgress 树(供下次更新使用)。
通过维护两棵 Fiber 树(current
和 workInProgress
),实现:
- 计算与渲染分离。
- 高优先级任务抢占。
- 无闪烁的 UI 更新。
最终提升复杂应用的流畅度和响应速度。