Fiber

7 阅读13分钟

React Fiber 是 React 16 引入的核心架构重构,解决了旧版栈调和(Stack Reconciliation)的性能瓶颈,是理解 React 并发更新、时间切片、Suspense 等特性的基础,也是中高级前端面试的高频考点。以下按「考察维度 + 高频题目 + 详细解析」整理,覆盖核心原理、工作流程、关键特性等

一、基础概念类(必问入门)

1. 什么是 React Fiber?为什么 React 16 要引入 Fiber?

问题解析:

考察 Fiber 的核心定位和设计背景,需先说明「Fiber 是什么」,再对比旧架构的问题,突出引入 Fiber 的必要性。

答案:

  • Fiber 的定义:Fiber 是 React 16 后的核心架构底层数据结构(可理解为「增强版虚拟 DOM 节点」),同时也是一套「可中断、可恢复、带优先级」的任务调度机制。从数据结构上,每个 Fiber 节点对应一个组件,存储了组件的类型、DOM 信息、指针(指向父 / 子 / 兄弟 Fiber)、任务优先级等;从调度机制上,Fiber 让 React 能将渲染任务拆分成小单元,按需暂停、恢复或终止,避免长时间阻塞主线程。

  • 引入 Fiber 的原因(旧架构痛点) :React 15 及之前使用「栈调和(Stack Reconciliation)」:

    1. 虚拟 DOM 比对是同步递归的,一旦开始就无法中断(递归调用栈会一直执行到结束);
    2. 若组件树较深,递归比对会占用主线程数百毫秒,导致浏览器无法响应鼠标、键盘输入等交互(掉帧);
    3. 无法区分任务优先级(比如用户输入、动画等紧急任务,和数据请求后的渲染等非紧急任务,优先级相同)。

    Fiber 正是为了解决「同步阻塞」和「无优先级调度」问题,让 React 具备「并发渲染」能力。

2. Fiber 的核心目标是什么?

答案:

Fiber 的三大核心目标,直接对应旧架构的痛点:

  1. 可中断(Interruptible) :将渲染任务拆分成小单元,执行过程中可暂停;
  2. 可恢复(Resumable) :暂停后能根据优先级恢复执行,不丢失之前的工作进度;
  3. 带优先级(Prioritized) :为不同任务分配优先级,紧急任务(如用户输入、动画)优先执行,非紧急任务(如列表渲染)延后执行。

3. Fiber 与 虚拟 DOM 的关系是什么?

答案:

  • 虚拟 DOM 是「描述组件结构的抽象数据」(如 { type: 'div', props: {}, children: [] }),Fiber 是「基于虚拟 DOM 构建的、用于调度和渲染的底层数据结构」;
  • 每个虚拟 DOM 节点都会对应一个 Fiber 节点(一一映射),Fiber 节点在虚拟 DOM 信息的基础上,新增了「调度相关属性」(如优先级、指针);
  • 虚拟 DOM 关注「组件结构描述」,Fiber 关注「任务调度和渲染流程控制」,Fiber 是虚拟 DOM 与 React 调度机制之间的桥梁。

二、核心原理类(重点考察)

1. Fiber 的数据结构有哪些关键属性?(可结合源码简要说明)

问题解析:

考察对 Fiber 节点底层设计的理解,无需背全源码属性,重点说核心作用的属性。

答案:

Fiber 节点是一个 JS 对象,核心属性及作用如下(简化版):

javascript

运行

const FiberNode = {
  // 1. 组件相关信息
  type: 'div', // 组件类型(如 div、FunctionComponent、ClassComponent)
  props: {}, // 组件接收的 props
  stateNode: null, // 对应的真实 DOM 节点(或组件实例)

  // 2. 指针:构建 Fiber 树的链表结构(替代旧架构的递归)
  return: parentFiber, // 父 Fiber 节点(向上指针)
  child: childFiber, // 第一个子 Fiber 节点(向下指针)
  sibling: nextSiblingFiber, // 下一个兄弟 Fiber 节点(同级指针)

  // 3. 调度相关信息
  priority: LanePriority, // 任务优先级(React 18 用 Lane 模型表示)
  flags: Placement | Update, // 标记当前 Fiber 的操作(如插入、更新、删除)
  alternate: workInProgressFiber, // 双缓存机制:指向对应的「工作 Fiber 节点」
};

核心属性作用:

  • 「指针(return/child/sibling)」:将递归的组件树改成「链表结构」,支持遍历中断和恢复(递归无法中断,链表可随时停止遍历);
  • 「priority」:标记任务优先级,调度器根据优先级决定执行顺序;
  • 「flags」:标记组件需要执行的 DOM 操作(如新增、更新),提交阶段批量执行;
  • 「alternate」:支持双缓存机制,避免 DOM 操作冲突,提升渲染性能。

2. Fiber 为什么用「链表结构」替代旧架构的「递归」?

答案:

核心原因是「支持任务中断和恢复」:

  • 旧架构的递归调用栈是「函数调用栈」,一旦进入递归,必须执行到所有子组件比对完成才能退出,无法中断;
  • Fiber 将组件树改成「链表结构」,通过「父→子→兄弟」的指针遍历组件树(而非递归),遍历过程中可随时暂停(比如执行高优先级任务),暂停时只需记录当前遍历的 Fiber 节点,恢复时从该节点继续遍历,无需重新开始;
  • 链表结构是实现「时间切片」的基础。

3. 什么是「时间切片(Time Slicing)」?Fiber 如何实现时间切片?

答案:

  • 时间切片定义:将一次长任务(如组件树比对)拆分成多个「短任务单元」(每个单元执行时间 ≤ 16ms,因为浏览器每秒刷新 60 帧,每帧约 16.6ms),执行完一个单元后,释放主线程让浏览器处理交互、动画等,再继续执行下一个单元,避免掉帧。

  • Fiber 实现时间切片的核心逻辑

    1. 利用浏览器的 requestIdleCallback API(React 内部做了 polyfill,因为兼容性和触发频率问题),在浏览器「空闲时间」执行任务单元;
    2. 每次执行一个 Fiber 节点的比对(任务单元)后,检查是否超时(超过 16ms)或有更高优先级任务;
    3. 若超时 / 有高优先级任务,暂停当前任务,记录进度;
    4. 等浏览器下次空闲或高优先级任务完成后,恢复之前的任务进度,继续执行下一个单元。

4. Fiber 的「双缓存机制」是什么?作用是什么?

问题解析:

双缓存是 Fiber 渲染流程的核心优化,必须讲清「两个树」的作用和切换逻辑。

答案:

  • 双缓存定义:React 同时维护两棵 Fiber 树,通过「树切换」实现高效渲染:

    1. current 树:当前页面显示的 Fiber 树,与真实 DOM 一一对应;
    2. workInProgress 树:正在构建的新 Fiber 树(基于最新的组件状态),构建过程中不会操作真实 DOM。
  • 核心流程

    1. 初始渲染时,React 构建 workInProgress 树,完成后将 current 树 指向它,同时根据 workInProgress 树 生成真实 DOM 并渲染;
    2. 组件状态更新时(如 setState),React 基于 current 树 复制一份新的 workInProgress 树,在新树上进行比对、标记操作(如 Placement Update);
    3. 新树构建完成后,React 触发「提交阶段」,将 workInProgress 树 切换为 current 树,同时批量执行标记的 DOM 操作;
    4. 旧的 current 树 被废弃,等待垃圾回收。
  • 作用

    1. 避免「边构建边渲染」导致的 DOM 操作冲突和页面抖动;
    2. 构建过程中即使被中断,也不会影响当前页面的正常显示(因为操作的是 workInProgress 树);
    3. 提交阶段批量执行 DOM 操作,提升渲染性能。

5. Fiber 的「优先级调度」是如何实现的?有哪些优先级类型?

答案:

  • 核心原理:Fiber 引入「优先级机制」,让紧急任务打断非紧急任务,核心依赖 React 的 Scheduler(调度器)模块:

    1. 每个 Fiber 任务都有一个优先级标记(React 18 用「Lane 模型」,之前用「Expiration Time」);
    2. 调度器维护一个优先级队列,每次只执行队列中优先级最高的任务;
    3. 低优先级任务执行过程中,若有高优先级任务进入队列,低优先级任务会被暂停,先执行高优先级任务;
    4. 高优先级任务执行完成后,低优先级任务可能被重新调度(若状态已过期)。
  • 常见优先级类型(从高到低)

    1. 「Immediate」:紧急任务(如用户输入、动画帧回调),必须立即执行;
    2. 「UserBlocking」:用户交互相关任务(如点击事件回调),优先级高,避免交互延迟;
    3. 「Normal」:普通任务(如组件渲染、数据更新),优先级中等;
    4. 「Low」:低优先级任务(如非紧急的列表渲染);
    5. 「Idle」:空闲任务(如日志上报),仅在浏览器完全空闲时执行。

三、工作流程类(核心重点)

1. React 基于 Fiber 的渲染流程分为哪几个阶段?每个阶段的作用是什么?

答案:

基于 Fiber 的渲染流程分为「调度阶段(Scheduler)」「协调阶段(Reconciliation)」「提交阶段(Commit)」三个核心阶段,其中前两个阶段支持中断,最后一个阶段不可中断。

阶段核心作用是否可中断
调度阶段接收更新任务(如 setStateuseState),计算任务优先级,决定是否执行该任务
协调阶段遍历 Fiber 树(链表遍历),对比新旧 Fiber 节点(Diff 算法),标记操作类型(Placement/Update/Deletion
提交阶段根据协调阶段的标记,批量执行真实 DOM 操作(插入、更新、删除),触发组件生命周期(如 componentDidMount)或 Hooks(如 useEffect

2. 协调阶段(Reconciliation)的具体流程是什么?和 Fiber 的关系是什么?

答案:

协调阶段是「Fiber 树比对和标记操作」的过程,核心流程:

  1. 初始化:从根 Fiber 节点开始,基于 current 树 构建 workInProgress 树

  2. 遍历 Fiber 树:通过「父→子→兄弟」的链表指针遍历每个 Fiber 节点(替代递归);

  3. Diff 比对:对每个节点进行新旧状态比对(如 props、state 变化):

    • 若节点类型不变(如都是 div 组件):更新节点属性,标记 Update 操作;
    • 若节点类型变化:标记 Deletion 操作(删除旧节点)和 Placement 操作(插入新节点);
    • 若节点不存在:标记 Deletion 操作;
  4. 递归子节点:处理完当前节点后,继续遍历子节点和兄弟节点,重复 Diff 过程;

  5. 记录进度:若遍历过程中被中断(超时 / 高优先级任务),记录当前遍历的 Fiber 节点,恢复时从该节点继续。

和 Fiber 的关系:协调阶段的「遍历中断」「Diff 标记」都依赖 Fiber 的链表结构和优先级属性,Fiber 是协调阶段的核心数据结构支撑。

3. 提交阶段(Commit)为什么不可中断?

答案:

提交阶段的核心是「执行真实 DOM 操作」,不可中断的原因:

  1. DOM 操作是「同步且原子性」的,一旦开始执行(如插入 DOM 节点),中途中断会导致 DOM 结构不完整,页面出现错乱;
  2. 提交阶段会触发组件生命周期或 Hooks(如 useEffect),这些回调函数的执行需要保证完整性,中断会导致状态不一致;
  3. 提交阶段的执行时间通常较短(因为协调阶段已完成大部分计算),不会长时间阻塞主线程。

四、进阶应用类(中高级面试)

1. Fiber 如何支持 React 的「并发模式(Concurrent Mode)」?

答案:

并发模式是 React 的一种渲染模式,允许组件「暂停渲染、恢复渲染或放弃渲染」,其底层完全依赖 Fiber 架构:

  1. Fiber 的「可中断、可恢复」特性,让 React 能暂停一个低优先级的渲染任务(如列表渲染),先执行高优先级任务(如用户输入);
  2. 并发模式下,React 可以「放弃」一个已开始的渲染任务(如组件状态已过期),重新开始新的渲染,而 Fiber 的双缓存机制确保放弃旧任务不会影响当前页面;
  3. 没有 Fiber 的可中断调度和双缓存,并发模式无法实现(旧架构同步递归渲染,无法暂停或放弃)。

2. Fiber 与 Hooks 的关系是什么?Hooks 依赖 Fiber 的哪些特性?

答案:

Hooks 是 React 16.8 引入的状态管理方案,其底层实现完全依赖 Fiber 架构:

  1. Hooks 存储位置:每个组件的 Hooks 链表(useState/useEffect 等)存储在对应 Fiber 节点的 memoizedState 属性中,Fiber 节点是 Hooks 的「容器」;
  2. Hooks 执行顺序:协调阶段遍历 Fiber 节点时,会按顺序执行该节点的 Hooks 链表,Fiber 的遍历顺序决定了 Hooks 的执行顺序(所以 Hooks 不能在条件语句中使用);
  3. 副作用调度useEffect 等副作用钩子的回调,会被标记在 Fiber 节点的 effectTag 中,提交阶段按优先级执行,依赖 Fiber 的调度机制。

3. 为什么 React 18 用「Lane 模型」替代之前的「Expiration Time」来表示优先级?

答案:

React 16/17 用「Expiration Time(过期时间)」表示优先级(如 performance.now() + 优先级对应的毫秒数),存在以下问题:

  1. 无法表示「多个同优先级任务」(如同时触发多个普通优先级更新,过期时间相同,无法区分执行顺序);
  2. 优先级计算复杂,容易出现「优先级反转」(低优先级任务过期后优先级高于高优先级任务);
  3. 不支持「批量更新」的精细控制。

React 18 引入「Lane 模型」(译为「车道模型」),核心改进:

  1. 用「二进制位」表示优先级(如最高优先级占 1 个二进制位,普通优先级占多个二进制位),支持「多个同优先级任务」并行调度;
  2. 优先级计算更简单,通过「位运算」快速判断任务优先级,避免优先级反转;
  3. 更好地支持并发模式和批量更新,能精细控制哪些任务可以批量执行,哪些需要立即执行。

4. 如何调试 Fiber 相关的问题?(如任务阻塞、优先级异常)

答案:

实际开发中调试 Fiber 相关问题,可借助以下工具和方法:

  1. React DevTools

    • 开启「Profiler」面板,录制组件渲染过程,查看每个任务的执行时间、优先级、中断次数;
    • 查看组件对应的 Fiber 节点信息(需开启「Debug → Show Fiber IDs」)。
  2. 控制台打印 Fiber 节点

    • 通过 ReactDOM.findDOMNode(componentRef)._reactInternalFiber(React 17+ 需用 __reactFiber$xxx 私有属性,注意生产环境不可用)打印 Fiber 节点,查看 priorityflagsalternate 等属性;
  3. 禁用并发模式

    • 若怀疑并发模式导致的问题,可通过 ReactDOM.createRoot(root).render(<App />) 改为 ReactDOM.render(<App />, root)(兼容模式),对比是否还存在问题;
  4. 使用 react-window 等库

    • 若列表渲染导致阻塞,可通过虚拟滚动减少 Fiber 节点数量,降低协调阶段的计算压力。

五、易错点与面试答题技巧

常见易错点

  1. 「Fiber 是新的虚拟 DOM」:错误!Fiber 是基于虚拟 DOM 的底层架构(数据结构 + 调度机制),不是替代虚拟 DOM;
  2. 「提交阶段可中断」:错误!提交阶段执行真实 DOM 操作,不可中断;
  3. 「时间切片依赖 requestIdleCallback 原生 API」:不完全正确!React 内部实现了 requestIdleCallback 的 polyfill(scheduler.dequeueTask),因为原生 API 触发频率低、兼容性差;
  4. 「Hooks 可以在条件语句中使用」:错误!Hooks 存储在 Fiber 节点的链表中,依赖遍历顺序,条件语句会打乱顺序,导致 Hooks 状态错乱。