React Fiber机制

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 树:在内存中构建的新树,代表下一次要显示的界面。

工作流程:

  1. 触发更新时,克隆 current 树生成 workInProgress 树。
  2. 在 workInProgress 树上进行协调(diff + 标记副作用)。
  3. 构建完成后,commitRoot 将 workInProgress 树切换为 current 树(一次指针切换)。
  4. 复用 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、标记副作用 ✅ 可中断 rendershouldComponentUpdate、hooks 中的更新计算
Commit 将副作用应用到真实 DOM、执行生命周期 ❌ 不可中断 componentDidMountuseLayoutEffectref 更新

重要区别

  • Render 阶段可以被打断(比如用户点击新的高优先级事件),React 会丢弃当前 workInProgress 树,重新开始。
  • Commit 阶段必须同步完成,确保 DOM 一致性和用户看到的结果。

这也解释了为什么 componentWillMountcomponentWillUpdate 被标记为不安全 ------ 它们可能在 Render 阶段被多次调用或中断。

四、Fiber 带来的具体能力

  1. Concurrent Mode(并发模式,现为默认启用):渲染和用户输入并行处理,不再互相阻塞。
  2. Suspense:等待异步数据(代码、图片、数据)时,渲染 fallback,数据就绪后继续 Fiber 工作。
  3. useDeferredValue / startTransition:将低优先级更新标记为可中断,保障高优先级响应的流畅度。
  4. 错误边界 :通过 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 上的存储原理,我可以继续展开。

相关推荐
小Q的编程笔记2 小时前
Pump.fun 的核心是什么?用 300 行 Solidity 实现 Bonding Curve 与自动 LP 销毁
前端·后端·智能合约
卷帘依旧3 小时前
JavaScript 判断页面加载完成的多种场景
前端
光影少年3 小时前
React 项目常见优化方案
前端·react.js·前端框架
lichenyang4533 小时前
把 demo 里的 console.log 全换成 HiLog:从 %{private} 没脱敏的困惑说起
前端
光影少年3 小时前
组件复用:HOC、Render Props、自定义Hook 对比
前端·react.js·掘金·金石计划
Gauss松鼠会3 小时前
【GaussDB】GaussDB SMP特性调优详解
java·服务器·前端·数据库·sql·算法·gaussdb
葬送的代码人生3 小时前
JavaScript 数组完全指南:从入门到实战
前端·javascript·算法
用户938515635074 小时前
深入理解 JavaScript 同步与异步:从单线程到事件循环与 Promise
前端·javascript
搬砖的码农4 小时前
造一个 Agent 运行时 #01:我决定开干,顺便把坑都写下来
前端·agent·ai编程