一、背景:为什么需要 Fiber?
1. React 15 时代的问题
在 React 15 及更早版本中,渲染是同步、不可中断的。当 setState 触发更新时,React 会递归整棵虚拟 DOM 树,计算出差异(diff),并一次性更新真实 DOM。
这种方式在小应用中没问题,但在复杂 UI 场景下会出现严重性能问题:
- 更新过程是 单线程、不可中断的;
- JavaScript 主线程被 React 占用时,用户交互、动画、滚动都会卡顿;
- React 无法根据任务的重要程度灵活调度(比如动画比日志输出更重要)。
📉 问题总结:
旧版 React 的渲染是「递归 + 同步」的。递归函数一旦开始执行,直到整棵树遍历完之前无法打断。这会导致主线程长时间被阻塞。
二、Fiber 的核心目标
1. 可中断的渲染
React Fiber 将同步的递归渲染改写为 可中断、可恢复的循环任务。这意味着:
- React 渲染工作可以被切成更小的片段(fiber),
- 每个片段在空闲时间内执行,
- 若有更高优先级任务(如用户输入),可以中断当前渲染。
🧠 目的: 不再让 React “霸占主线程”,而是让渲染像多任务调度一样可控。
2. 更精细的调度控制
Fiber 引入 优先级 (Priority) 概念,让不同类型的更新拥有不同的紧急程度。
| 更新类型 | 示例 | 优先级 |
|---|---|---|
| 用户交互 | 输入框打字 | 高 |
| 动画 | 组件过渡动画 | 中 |
| 网络请求结果更新 | 异步数据渲染 | 低 |
这样 React 就能合理分配时间,先响应用户、后处理耗时任务。
3. 持久的执行上下文
Fiber 将每个组件更新的执行信息(包括 props、state、children 等)封装为一个对象,这个对象就是 FiberNode。它可以在中断后继续恢复,不会丢失执行状态。
🌿 Fiber 的名字就来源于「纤维」:一种比线程(Thread)更轻量的执行单元。
三、Fiber 架构的核心思想
1. 从「递归」到「循环」
传统的递归渲染:
function render(element) {
reconcile(element);
render(element.children);
}
在 Fiber 架构中变成了手动管理的循环:
while (nextUnitOfWork) {
nextUnitOfWork = performUnitOfWork(nextUnitOfWork);
}
🌀 核心区别: React 不再让调用栈来管理执行顺序,而是自己保存「下一个任务指针」,从而实现可中断、可恢复的渲染调度。
2. Fiber Node(工作单元)
每个组件对应一个 FiberNode,它包含:
| 属性 | 说明 |
|---|---|
type | 对应的组件类型(函数 / class / 原生标签) |
child | 第一个子节点的引用 |
sibling | 下一个兄弟节点 |
return | 父节点(用于回溯) |
pendingProps | 本次渲染的新 props |
stateNode | 对应的 DOM 或组件实例 |
通过这些指针,React 可以实现「链式遍历」而不是「递归遍历」。
📘 比喻:
以前 React 是“从树顶一路递归下去”,现在是“从树顶拿着线条顺着走下去”。
3. 双缓存机制(Double Buffering)
Fiber 使用“双缓存”技术保证 UI 更新的原子性:
- 当前显示的 UI 树称为 current fiber tree;
- 正在构建的新树称为 workInProgress fiber tree;
- 当新树构建完成后,React 会一次性将其切换为 current。
这意味着:
UI 更新是“可中断的”,但“提交到屏幕”的过程依然是“原子的”。
🧩 好处: 不会出现“渲染一半”的画面,保证 UI 一致性。
四、Fiber 渲染流程(简化版)
🪄 阶段拆分:
- Render Phase(可中断) :构建 Fiber 树,计算变更。
- Commit Phase(不可中断) :将变更一次性应用到 DOM。
五、Fiber 架构解决了什么问题?
| 问题 | Fiber 解决方式 |
|---|---|
| 渲染阻塞主线程 | 将任务拆分为可中断单元 |
| 无法区分任务优先级 | 引入 lanes / expirationTime 优先级机制 |
| 状态更新无法暂停恢复 | FiberNode 保存上下文,实现可恢复渲染 |
| UI 更新不一致 | 双缓存保证渲染原子性 |
六、仍然存在的问题与挑战
- 调度粒度有限:即使拆成 Fiber 单元,某些更新仍然较重。
- 协调过程仍然昂贵:深层组件频繁更新仍可能导致重建成本高。
- 过多优先级切换可能造成抖动。
这些问题促使 React 继续演化出:
- 🧭 Concurrent Rendering(并发渲染) :通过 Scheduler 更智能地调度任务;
- ⚙️ Suspense / Transition APIs:控制更新优先级;
- 🚀 React Compiler(未来) :提前编译 Fiber 调度结构,减少运行时负担。
七、总结:Fiber 的设计哲学
| 维度 | 旧 React | 新 Fiber 架构 |
|---|---|---|
| 执行模式 | 递归、同步 | 循环、可中断 |
| 渲染阶段 | 单阶段 | Render + Commit 两阶段 |
| 任务管理 | 无 | Scheduler 管理优先级 |
| 状态保存 | 栈 | Fiber Node 链表 |
| 响应性能 | 可能卡顿 | 可让出主线程 |
🎯 一句话总结:
Fiber 是 React 从「函数式 UI」迈向「可调度、响应式 UI」的根本转折点。它不是为了更快地渲染,而是为了更聪明地渲染。
八、未来优化方向
- 减少无意义的 Fiber 构建 → 静态分析 + React Compiler。
- 提高 Scheduler 智能化程度 → 引入多队列调度。
- 支持 Web Worker 渲染 → 让 Fiber 工作线程脱离主线程。
- Server Components → 将部分渲染前移到服务端,减少前端 Fiber 压力。
🧭 牢记
-
理解 Fiber 的关键不是记代码,而是理解:
- 为什么要“可中断”?
- 为什么要“优先级”?
- 为什么要“双缓存”?
-
多画图、多比喻,体会 React 在“人机交互性能”层面的设计哲学。
Fiber 是 React 从「虚拟DOM」到「调度系统」的里程碑。它的核心不是 Diff,而是时间。