React面试宝典【Part3:Fiber架构 + Diff算法】
三、渲染原理:Fiber架构 + Diff算法
1. React16为什么推出Fiber架构?
老架构:Stack Reconciler,同步递归渲染,不可中断;组件树庞大时阻塞主线程,页面卡顿。
Fiber目标:
- 时间切片,任务可中断、恢复、优先级调度
- 将递归改为链表遍历(child/sibling/return指针)
- 拆分为两大阶段:Render阶段 + Commit阶段
Render阶段(可中断!纯内存计算,无DOM操作)
构建Fiber树、执行Diff、标记更新副作用(Placement/Update/Deletion)
Commit阶段(不可中断!操作真实DOM)
一次性执行所有DOM增删改;分为before mutation → mutation → layout
详解
1. 解决旧架构(Stack Reconciler)的性能瓶颈
- 旧版 React 递归更新虚拟 DOM 是同步、不可中断的
- 一旦开始 diff,就会一直占用主线程,直到执行完毕
- 当组件树庞大时,会阻塞浏览器渲染、交互、动画,导致页面卡顿、掉帧
2. 实现可中断、可恢复、可优先级调度的渲染
Fiber 把 diff 过程拆分为小任务单元,支持:
- 可中断:执行一段时间就让出主线程
- 可恢复:下次继续执行,不从头再来
- 可插队:高优先级任务(输入、点击)可以打断低优先级任务
- 可废弃:可直接放弃旧更新,执行新更新
3. 为并发模式(Concurrent Mode)铺路
Fiber 是 React 并发能力的底层基础:
- 支持 Suspense
- 支持 useTransition、useDeferredValue 等优先级调度
- 让 React 能根据设备性能、任务类型智能调度更新
4. 更精细的生命周期与副作用调度
- 把渲染分为 render 阶段(可中断) 和 commit 阶段(不可中断)
- render 阶段:diff、构建 Fiber 树
- commit 阶段:真实 DOM 更新、执行 useEffect/useLayoutEffect
总结
旧架构递归同步阻塞主线程,Fiber 把更新拆分为可中断的小任务,实现时间分片与优先级调度,从根本解决大型应用卡顿问题,并为 React18 并发特性打下基础。
2. Diff算法三大策略(协调Reconciliation/ ˌrekənsɪliˈeɪʃ(ə)n /)
- 只同级比较,不跨层级移动节点;跨层级直接销毁重建
- 同类型节点:更新属性;不同类型:直接销毁重建
- key作为节点唯一标识,实现列表节点复用
面试官追问:能不能实现跨层级Diff?可以,但算法复杂度O(n³),性能无法接受,React选择工程权衡。
详解
1. 同级比较,不跨层级 Diff
- 只对同一层级的节点进行对比
- 若节点类型变了,直接销毁重建,不继续往下 diff
- 避免 O(n³) 复杂度,降到 O(n)
2. 不同类型节点,生成不同树
- 标签名/组件类型不同 → 认为是完全不同的树
- 直接删除旧节点,创建新节点,不做复用
- 防止复杂结构对比,大幅提速
3. 列表 Diff 通过 key 区分节点
- 列表渲染必须带 key
- React 根据 key 匹配新旧节点,实现移动、复用、删除
- 避免盲目重建所有列表项
总结
只同级 diff、类型不同就重建、列表靠 key 复用。
目的:把暴力 Diff 从 O(n³) 优化到 O(n)。
3. React 协调(Reconciler)完整流程(Fiber 版)
触发更新(setState/useState)→ 创建更新任务 → Scheduler调度 → Render(构建Fiber+diff打标记)→ Commit阶段执行DOM更新
详解
一、触发更新
- setState、useState dispatch、forceUpdate、ReactDOM.render
- 标记对应 Fiber 节点为 dirty(待更新)
- 进入调度阶段
二、调度阶段(Scheduler)
- 根据任务优先级(高:输入/点击;低:列表渲染)进行调度
- React18 基于 时间分片,每帧让出主线程,避免卡顿
- 高优先级任务可插队、打断低优先级任务
三、协调阶段(Render 阶段)——可中断、可恢复
深度遍历 Fiber 树,执行:
-
beginWork(向下遍历)
- 创建/更新 Fiber 节点
- 执行组件 render,生成新 VDOM
- Diff 子节点,打上 effectTag(新增/删除/更新)
-
completeWork(向上归并)
- 处理 props、style、event
- 收集副作用(Effect List)
- 形成一条单向副作用链表,供 commit 阶段批量执行
特点:
- 可中断、可恢复、可重制
- 不操作真实 DOM
- 生命周期/Hook 在此阶段可能被多次调用
四、提交阶段(Commit 阶段)——同步不可中断
一次性批量更新 DOM,分三步:
-
commitBeforeMutationEffects
- 执行 DOM 更新前操作
- 处理
useLayoutEffect清理
-
commitMutationEffects
- 操作真实 DOM:增删改
- 重置文本、属性、样式
- 切换 ref
-
commitLayoutEffects
- DOM 已更新,执行布局副作用
- 执行
useLayoutEffect回调 - 最后执行
useEffect(异步调度)
五、结束
- 清空副作用队列
- 组件更新完成
- 进入下一帧渲染
总结
- 触发更新 → 标记 Fiber 为脏
- 调度优先级 → 时间分片
- Render 协调:beginWork 遍历 + Diff 收集副作用,可中断
- Commit 提交:批量同步更新 DOM,执行 Layout/Effect
- 完成渲染