React Fiber 是 React 16 引入的核心协调算法 重构,它解决了旧版 Stack Reconciler 的不可中断、同步递归导致的卡顿问题。
简单来说:Fiber 让 React 的渲染过程从「一口气干完 」变成了「可中断、可分片、可优先级排序」的任务调度。
下面从三个关键维度展开:
一、为什么需要 Fiber?(痛点分析)
旧版 React(<16)的 Stack Reconciler 有致命缺陷:
- 同步递归:遍历整个虚拟 DOM 树时,一旦开始就无法停止。
- 阻塞主线程:如果组件层级很深(几百个),一次更新可能占用 100ms+ 的 JS 执行时间。
- 导致掉帧:浏览器需要 16.6ms 渲染一帧(60fps),而 React 长期霸占主线程,用户点击、动画都会卡顿。
典型案例:一个输入框绑定大列表更新,每次按键都重新渲染整个列表,造成明显延迟。
Fiber 的目标就是:把同步不可中断的工作,拆解成异步可中断的小任务单元。
二、Fiber 的核心机制
1. Fiber 是什么?(数据结构 + 工作单元)
Fiber 既是数据结构 ,也是执行单元:
-
作为数据结构:每个 React 元素对应一个 Fiber 节点,构成 Fiber 树(链表结构,而非树)。
javascript// Fiber 节点的核心字段 { tag: 5, // 节点类型(函数组件、类组件、DOM等) key: null, type: 'div', return: parentFiber, // 指向父 Fiber(完成后的返回目标) child: childFiber, // 指向第一个子 Fiber sibling: siblingFiber,// 指向下一个兄弟 Fiber pendingProps: {}, // 新传入的 props memoizedProps: {}, // 上次渲染的 props memoizedState: null, // 上次渲染的 state(用于 Hooks) alternate: null, // 双缓存中的另一个 Fiber(current <-> workInProgress) } -
作为执行单元:Fiber 节点记录了自身的「进度信息」,React 可以在中断后从任意 Fiber 继续工作。
2. 双缓存机制(Double Buffering)
React 维护两棵 Fiber 树:
- current 树:当前屏幕上显示的内容(已提交到 DOM)。
- workInProgress 树:在内存中构建的新树,代表下一次要显示的界面。
工作流程:
- 触发更新时,克隆 current 树生成 workInProgress 树。
- 在 workInProgress 树上进行协调(diff + 标记副作用)。
- 构建完成后,
commitRoot将 workInProgress 树切换为 current 树(一次指针切换)。 - 复用
alternate属性指向另一棵树,节省内存分配。
好处:可以在构建过程中随时放弃 workInProgress 树,不影响用户当前看到的界面。
3. 调度与优先级(Scheduler)
Fiber 搭配了独立的调度器(Scheduler),实现了时间切片和优先级队列。
- 任务拆分:将协调过程拆解为多个「工作单元」(每个 Fiber 节点的处理就是一个单元)。
- 时间切片 :每次循环检查是否还有剩余时间(
shouldYield),超时则中断并让出主线程。 - 优先级机制 :
- 离散事件(click、input):立即同步执行(最高优先级)。
- 连续事件(scroll、拖拽):下一帧之前执行(高优先级)。
- 数据请求(useEffect、Suspense fallback):可中断的低优先级。
startTransition标记的更新:最低优先级,空闲时执行。
简单模拟调度流程:
javascript
// 简化的 workLoop
function workLoop(deadline) {
let shouldYield = false;
while (nextUnitOfWork && !shouldYield) {
nextUnitOfWork = performUnitOfWork(nextUnitOfWork); // 处理一个 Fiber
shouldYield = deadline.timeRemaining() < 1; // 剩余时间不足 1ms 则中断
}
if (nextUnitOfWork) {
requestIdleCallback(workLoop); // 有空闲时间再继续
} else {
commitRoot(); // 全部完成,提交 DOM
}
}
requestIdleCallback(workLoop);
三、Fiber 架构下的两个阶段
React 将渲染过程拆解为两个截然不同的阶段:
| 阶段 | 执行内容 | 是否可中断 | 是否执行副作用 | 常见方法 |
|---|---|---|---|---|
| Render/Reconciliation | 构建 Fiber 树、计算 diff、标记副作用 | ✅ 可中断 | ❌ | render、shouldComponentUpdate、hooks 中的更新计算 |
| Commit | 将副作用应用到真实 DOM、执行生命周期 | ❌ 不可中断 | ✅ | componentDidMount、useLayoutEffect、ref 更新 |
重要区别:
- Render 阶段可以被打断(比如用户点击新的高优先级事件),React 会丢弃当前 workInProgress 树,重新开始。
- Commit 阶段必须同步完成,确保 DOM 一致性和用户看到的结果。
这也解释了为什么 componentWillMount 和 componentWillUpdate 被标记为不安全 ------ 它们可能在 Render 阶段被多次调用或中断。
四、Fiber 带来的具体能力
- Concurrent Mode(并发模式,现为默认启用):渲染和用户输入并行处理,不再互相阻塞。
- Suspense:等待异步数据(代码、图片、数据)时,渲染 fallback,数据就绪后继续 Fiber 工作。
- useDeferredValue / startTransition:将低优先级更新标记为可中断,保障高优先级响应的流畅度。
- 错误边界 :通过
componentDidCatch捕获子组件树的错误,防止整个应用崩溃。
五、常见面试追问与简答
Q:Fiber 架构下的 diff 算法有变化吗?
A:核心策略相同(同级比较、key 复用),但实现上基于 Fiber 链表,且 diff 过程是异步可中断的。
Q:Fiber 有打破 React 声明式编程的范式吗?
A:没有。Fiber 是内部实现优化,对外 API 保持一致,开发者依然面向状态编程。
Q:使用 Fiber 一定能提升性能吗?
A:不一定。对于简单应用,Fiber 的调度开销可能略微增加总耗时。主要收益是在复杂交互场景(输入框 + 大列表渲染)下保持 UI 流畅。
Q:Fiber 树和虚拟 DOM 树是什么关系?
A:虚拟 DOM 是 React 元素树(每次 render 重建)。Fiber 树是持久化的(只更新属性),通过 alternate 复用节点,性能更高。
如果你需要进一步了解 Fiber 的副作用收集(Effect List) 或Hooks 在 Fiber 上的存储原理,我可以继续展开。