资讯动态

React Fiber:从“解决卡顿”到完整更新链路

发布时间:2026/10/3 7:30:23 来源:尧图企业网站定制
一、面试题React Fiber 是什么为什么要引入 Fiber核心思路一句话Fiber 本质是把 React 渲染工作拆成可中断、可恢复、可调度的任务从而让 React 能按优先级控制渲染时机。主要矛盾旧版 React 的问题不是“虚拟 DOM 慢”而是一次更新过程中大量 Reconciliation 工作在 JS 主线程上连续执行无法及时让出主线程。例如用户点击 ↓ setState ↓ 同步执行大量组件更新 ↓ Diff ↓ 生成 DOM 操作 ↓ JS 长任务持续占用主线程 ↓ 浏览器无法及时处理输入 / 动画 / 绘制 ↓ 页面卡顿Fiber 将其改造成状态更新 ↓ 创建/标记更新任务 ↓ Scheduler 调度优先级 ↓ Fiber Reconciliation ↓ 可中断 / 可恢复 / 可重新开始 ↓ 生成副作用 Flags ↓ Commit ↓ 真实 DOM 更新二、面试题Fiber 只是为了解决“大列表渲染卡顿”吗核心思路一句话不是大列表只是 Fiber 能力的一个应用场景Fiber 真正解决的是 React 渲染工作的“可调度性”。Fiber 带来的核心能力Fiber ├── 工作单元化 ├── 可中断 ├── 可恢复 ├── 可重新开始 ├── 优先级调度 ├── 并发渲染 ├── Suspense ├── Transitions └── 更细粒度地控制 Render / Commit所以面试时不要回答“Fiber 就是用来做时间切片的。”更准确的回答是Fiber 把 React 的渲染过程拆成 Fiber 工作单元使 React 可以暂停、恢复、丢弃和重新执行 Render 工作并结合 Scheduler 对不同更新进行优先级调度。三、面试题Fiber 如何实现时间切片核心思路一句话把原本连续执行的大量 Fiber 工作拆成多个小工作单元每处理一部分就检查是否应该让出主线程。Fiber 为什么天然适合拆分传统递归render(A) └─ render(B) └─ render(C) └─ render(D) └─ ...如果递归过程很深开始 ↓ A ↓ B ↓ C ↓ D ↓ ... ↓ 全部完成中途很难暂停。Fiber 把节点组织成类似链表的结构Fiber ├── child ├── sibling └── return例如A / \ B C / \ \ D E F可以转换成A ↓ child B ↓ child D ↓ sibling E ↓ return B ↓ sibling C ↓ child F ...因此 React 可以performUnitOfWork(A) ↓ performUnitOfWork(B) ↓ performUnitOfWork(D) ↓ 检查是否需要让出 ↓ 暂停 ↓ 之后继续 E四、面试题时间切片是不是意味着 React 一定不会卡顿核心思路一句话不是。时间切片只能让 React 的 Render 工作具备让出主线程的能力无法阻止其他同步 JavaScript 长任务也无法让 Commit 阶段无限制地中断。这是非常重要的追问。例如while(true){// 同步 JS 长任务}浏览器主线程已经被你的 JS 占满你的同步 JS ████████████████████████████████ ↑ React 无法运行React 的调度器根本没有机会执行。所以React Fiber 能调度 React 自己的工作但不能抢占任意同步 JavaScript。五、面试题哪些工作可以被中断哪些工作不能被中断核心思路一句话Render 阶段可以被中断、恢复甚至丢弃Commit 阶段需要保持 UI 更新的一致性因此不能像 Render 一样随意中断。Render 阶段主要工作组件执行 ↓ Reconciliation ↓ 创建 / 复用 Fiber ↓ Diff ↓ 计算 Flags ↓ 形成新的 Fiber Tree特点可中断 可恢复 可重新开始 旧 Render 结果可以被丢弃Commit 阶段主要工作Before Mutation ↓ Mutation ↓ Layout例如DOM 插入 DOM 删除 DOM 属性修改 useLayoutEffect ref 更新Commit 必须保证一旦开始提交就要让 UI 从旧状态一致地过渡到新状态。因此不能简单理解成Render可中断 Commit不可中断更准确是Render 是可协作调度的计算阶段Commit 是需要保持 UI 一致性的同步提交阶段。六、面试题React 从 setState 到真实 DOM 更新完整链路是什么核心思路一句话一次更新可以概括为Update → Scheduler → Render/Reconciliation → Fiber Tree → Flags → Commit → DOM。完整流程图用户操作 / 网络数据 / setState ↓ 创建 Update ↓ 加入 UpdateQueue ↓ 调度 Root 更新 ↓ Scheduler ↓ 根据优先级安排任务 ↓ Render 阶段开始 ↓ BeginWork / CompleteWork ↓ Reconciliation ↓ 创建 / 复用 Fiber ↓ 标记 Flags ↓ 构建新的 Fiber Tree ↓ Render 完成 ↓ Commit 阶段 ↓ ┌───────────┼────────────┐ ↓ ↓ ↓ Before Mutation Layout Mutation ↓ ↓ ↓ 读取 DOM 修改 DOM 执行 Layout 相关信息 插入/删除/更新 Effects ↓ 浏览器后续绘制这里最容易混淆的是Render ≠ DOM 更新Render 主要是在计算“最终应该变成什么样。”Commit 才是真正“把计算结果应用到真实 DOM。”七、面试题BeginWork 和 CompleteWork 分别做什么核心思路一句话BeginWork 负责“向下处理节点、计算子树”CompleteWork 负责“向上完成节点、收集完成信息”。可以记成BeginWork 往下 CompleteWork 往上1. BeginWork主要职责当前 Fiber ↓ 处理组件更新 ↓ 执行组件逻辑 ↓ Reconciliation ↓ 生成 / 复用 children ↓ 返回下一个 child例如A ├── B └── C大致BeginWork(A) ↓ 产生 B、C ↓ BeginWork(B) ↓ BeginWork(C)2. CompleteWork当子节点处理完后向上返回D ↑ CompleteWork(D) ↑ CompleteWork(B) ↑ CompleteWork(A)它主要负责完成当前 Fiber ↓ 处理 Host Component ↓ 准备 DOM 相关信息 ↓ 处理 / 汇总 Flags ↓ 向父节点返回最终形成A / \ B C / \ D E BeginWork ↓ A → B → D → E → C CompleteWork ↑ D → E → B → C → A八、面试题Fiber 为什么使用 child / sibling / return而不是普通树结构核心思路一句话Fiber 使用“child sibling return”把树转换成可逐步遍历的结构使 React 可以把每个 Fiber 作为独立工作单元进行调度。fiber.child fiber.sibling fiber.return含义child → 第一个子节点 sibling → 下一个兄弟节点 return → 父节点例如A / | \ B C D / \ E FFiber 关系A.child B B.sibling C C.sibling D B.child E E.sibling F B.return A C.return A D.return A E.return B F.return B这使 React 可以使用向下child 横向sibling 向上return完成非递归式的工作循环。九、面试题React 如何判断 Fiber 节点应该复用还是创建核心思路一句话核心比较条件是同层级节点的key type匹配则复用 Fiber不匹配则创建新 Fiber。例如div key1 /更新div key1 /通常可以复用。但是div key1 /更新成span key1 /类型发生变化key 相同 type 不同 ↓ 不能直接复用 ↓ 删除旧节点 创建新节点列表中items.map(item ( Item key{item.id} / ))key的核心意义就是帮助 React 判断同层级节点的身份从而尽可能复用 Fiber。十、面试题Fiber 的 Flags 是什么核心思路一句话Flags 是 Fiber 上记录“这个节点在 Commit 阶段需要执行什么操作”的标记。Render 阶段发现变化 ↓ Fiber.flags 某些操作标记Commit 阶段读取 flags ↓ 执行对应 DOM 操作可以理解成Render “我发现这个节点需要更新” ↓ Flags “UPDATE” ↓ Commit “真正修改 DOM”常见概念包括Placement Update ChildDeletion Ref Passive Layout ...实际版本中的 Flags 集合会随着 React 内部实现变化因此面试不要死背某一版本的完整常量表。十一、面试题React 的同步更新和并发更新有什么区别核心思路一句话核心区别不是“有没有多线程”而是 React 是否允许这次 Render 工作被中断、延后、重新开始并根据优先级安排执行。同步更新可以理解成更新发生 ↓ 尽快完成 Render ↓ Commit并发更新可以理解成更新 ↓ 进入调度系统 ↓ 根据优先级安排 ↓ 开始 Render ↓ 发现更高优先级任务 ↓ 暂停当前工作 ↓ 处理高优先级任务 ↓ 之后继续 / 重新计算低优先级任务注意React Concurrent Rendering 不是多线程并行执行 React 代码。它主要仍然运行在JavaScript 主线程所谓“并发”更接近多个工作之间可以交错推进而不是多个 CPU 线程同时执行。十二、面试题useTransition 到底改变了什么核心思路一句话useTransition的核心不是“让代码执行更快”而是把某些状态更新标记为非紧急更新让 React 可以以较低优先级、可中断的方式处理。例如const[isPending,startTransition]useTransition();functionhandleChange(value){setInput(value);startTransition((){setSearch(value);});}这里setInput ↓ 紧急更新 ↓ 优先响应用户输入 setSearch ↓ Transition 更新 ↓ 可以延后 / 中断所以useTransition 不是 “把 JS 执行速度提高” 而是 “改变更新的调度优先级与可中断性”十三、面试题为什么 React 优先级无法解决同步阻塞 JavaScript核心思路一句话因为 React Scheduler 本身也运行在 JavaScript 主线程上主线程被同步长任务占满时Scheduler 根本没有执行机会。例如startTransition((){setState(...);});while(true){// 阻塞主线程}此时JS 主线程 ██████████████████████████████ ↑ 同步任务一直占用 Scheduler × 无法执行 React × 无法获得执行机会 浏览器输入 × 无法及时响应所以性能优化不能只盯着 ReactReact 调度优化 业务 JS 长任务优化 组件渲染优化 DOM 数量优化 浏览器渲染优化十四、面试题React Fiber 会不会出现“饥饿”问题核心思路一句话会。低优先级任务如果长期被高优先级更新打断就可能长期得不到执行因此 React 的调度系统需要处理任务过期、优先级提升等问题。抽象理解高优先级任务 ↓ 执行 高优先级任务 ↓ 执行 高优先级任务 ↓ 执行 低优先级任务 ↓ 一直等待这就是Starvation 饥饿React Scheduler 会通过任务优先级、expiration 等机制避免低优先级任务无限等待。十五、面试题Suspense 和 Fiber 调度是什么关系核心思路一句话Suspense 本身不是 Scheduler但它依赖 Fiber 的并发渲染能力让 React 能够在子树暂时无法完成时暂停这部分 UI并继续处理其他工作。抽象流程Render ↓ 进入 Suspense 子树 ↓ 子树暂时无法完成 ↓ Suspense 捕获挂起状态 ↓ 显示 fallback ↓ 其他高优先级工作继续 ↓ 数据准备完成 ↓ 重新调度 ↓ 重新 Render ↓ Commit 正常 UI所以不要说“Suspense 就是 Fiber 的一个 API。”更准确Suspense 是 React 的 UI 协调机制它建立在 Fiber 的可中断、可恢复渲染能力之上。十六、面试题Strict Mode 为什么会看到组件执行两次核心思路一句话开发环境下 React Strict Mode 会主动进行额外的调用/挂载检查以暴露不安全的副作用和不纯逻辑不能简单理解成“Fiber 把组件构建了两遍”。例如开发环境可能看到组件函数执行 ↓ 再次执行或者Effect ↓ cleanup ↓ Effect其目的主要是帮助发现不纯渲染 错误副作用 缺失 cleanup 依赖外部可变状态注意不要把 Strict Mode 的开发行为直接等同于生产环境真的执行两遍这是面试中的常见陷阱。十七、面试题为什么时间切片不是“切得越细越好”核心思路一句话切片本身有调度、保存状态、恢复执行等开销过度拆分会增加调度成本真正目标是让长任务获得合理的让出机会而不是无限细分。可以理解成任务太大 ↓ 主线程长时间阻塞 ↓ 卡顿但是任务切得过碎 ↓ 频繁调度 ↓ 上下文切换 / 调度开销增加 ↓ 整体吞吐下降所以优化目标是不是 “切得越碎越好” 而是 “在响应性与吞吐量之间取得平衡”十八、面试题Fiber 任务和浏览器的渲染帧是完全对齐的吗核心思路一句话不是。Fiber 的工作调度与浏览器帧调度有关联但不是“一次 Fiber 一帧”React 也不能保证每个 Fiber 工作单元严格对应一个浏览器帧。应该理解成React Scheduler ↓ 安排 React 工作 ↓ JavaScript 主线程 ↓ 浏览器事件循环 ↓ 样式计算 ↓ 布局 ↓ 绘制React 需要尽可能避免长期占满主线程但React Scheduler ≠ 浏览器渲染器二者属于不同层次。十九、面试题为什么 Commit 阶段不能像 Render 一样随便中断核心思路一句话因为 Render 是“计算结果”Commit 是“修改外部世界”如果 Commit 随意中断DOM 可能处于半更新状态破坏 UI 一致性。例如旧 DOM A B CCommit删除 A 修改 B 插入 D如果执行到删除 A 修改 B突然暂停A ❌ B ✅ C D ❌此时 UI 处于中间状态。因此 React 的设计是Render ↓ 可以暂停 ↓ 计算出完整结果 ↓ Commit ↓ 一次性完成必要的 DOM 提交这也是为什么面试中一定要区分Render 可中断 ≠ 整个 React 更新过程都可中断。二十、最终React Fiber 五层面试追问图React Fiber │ ┌───────────────┼────────────────┐ ↓ ↓ ↓ 第一层 第二层 第三层 基础概念 调度机制 Fiber 原理 │ │ │ 为什么需要 Fiber 优先级 BeginWork 时间切片是什么 同步/并发 CompleteWork 是否一定不卡顿 useTransition child/sibling/return │ │ Flags └───────────────┼────────────────┘ ↓ 第四层 并发陷阱 │ ┌─────────┼─────────┐ ↓ ↓ ↓ Suspense StrictMode 饥饿 │ ↓ 第五层 浏览器性能模型 │ ┌─────────┼──────────┐ ↓ ↓ ↓ 主线程 Render Commit │ │ │ 长任务 可中断 不可随意中断 │ ↓ 浏览器最终绘制满分答案面试时直接这样答React Fiber 本质上是 React 对渲染工作的重新组织和调度机制。它把组件树转换成 Fiber 工作单元使 Render 阶段可以被中断、恢复、重新开始并结合 Scheduler 根据更新优先级进行调度。一次更新大致经历 Update → Scheduler → Render/Reconciliation → BeginWork → CompleteWork → Flags → Commit → DOM。其中 Render 阶段主要负责计算新的 Fiber Tree可以被中断BeginWork 向下处理组件和子节点CompleteWork 向上完成节点并处理相关信息Flags 则记录 Commit 阶段需要执行的 Placement、Update、Deletion 等操作。Render 完成后进入 Commit真正执行 DOM 修改以及相关 Effects。Commit 为了保证 UI 一致性不能像 Render 一样随意中断。所以 Fiber 不只是解决大列表卡顿也不等于时间切片。它真正解决的是 React 渲染工作的可调度性为 Concurrent Rendering、Transition、Suspense 等能力提供了基础。另外Fiber 并不是多线程React Scheduler 也不能抢占任意同步 JavaScript。如果主线程被业务代码的长任务占满React 本身也无法获得执行机会。这道题真正要抓住的主线Fiber ↓ 工作单元化 ↓ Render 可中断 ↓ Scheduler 控制优先级 ↓ Reconciliation 计算新 Fiber Tree ↓ Flags 记录变化 ↓ Render 完成 ↓ Commit 同步提交 ↓ 真实 DOM面试真正的分水岭不是背“Fiber 时间切片”而是能不能把这条链路讲完整并解释清楚为什么 Render 能中断、为什么 Commit 不能随意中断、优先级到底改变了什么以及 React 为什么仍然无法解决任意同步 JS 长任务。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑