React的Fiber架构

Fiber 的核心目标------把一次"不可中断的递归渲染",拆成"可中断、可恢复、可优先级调度的工作单元"

一、为什么 React 要引入 Fiber?

老架构(Stack Reconciler)的问题,React 16 之前:

  • diff + render 是一次性同步递归
  • JS 线程被长期占用
  • 浏览器:
    • ❌ 不能响应用户输入
    • ❌ 不能渲染动画
    • ❌ 会卡顿、掉帧

📌 本质问题:React 没法"暂停 / 打断 / 恢复"渲染

二、核心概念

Fiber 是 React 内部对"组件/DOM 节点"的一种可调度的执行单元(Work Unit)

不是虚拟 DOM 本身,而是:

  • 虚拟 DOM + 运行时控制信息
  • 数据结构 + 执行上下文

三、Fiber 的核心数据结构

一个 Fiber 本质是一个对象:

TypeScript 复制代码
interface Fiber {
  // 节点类型
  tag: WorkTag;

  // 对应的组件或 DOM
  type: any;
  stateNode: any;

  // Fiber 树结构
  return: Fiber | null;  // 父
  child: Fiber | null;   // 第一个子
  sibling: Fiber | null; // 下一个兄弟

  // 状态相关(Hooks 在这)
  memoizedState: any;
  updateQueue: UpdateQueue<any> | null;

  // props
  pendingProps: any;
  memoizedProps: any;

  // 副作用
  flags: Flags;
  subtreeFlags: Flags;

  // 双缓存
  alternate: Fiber | null;
}

一些关键点:

  1. 树结构是 链表式的,为了能中断遍历,而不是递归调用栈
Plain 复制代码
child → sibling → sibling
  1. alternate:Fiber 的灵魂,"并发渲染不撕裂 UI"的关键
Plain 复制代码
current Fiber  ←→  workInProgress Fiber
  • current:当前屏幕显示的
  • wip:正在计算的新版本
  1. Hooks 存在哪里?useState 不是存在闭包,而是存在 Fiber 上
TypeScript 复制代码
fiber.memoizedState // Hooks 单向链表

四、Fiber 架构下的两大阶段

这两个阶段正是组件渲染的两个阶段:

  1. 父组件渲染导致子组件渲染(默认行为)
  2. 组件内状态更新
  3. context 改变导致使用 useContext 的组件更新

1️⃣ Render 阶段(Reconciliation 协调阶段)

做什么?

  • 创建 / 复用 Fiber(diff 过程)
  • 执行函数组件
  • 计算新 state,运行 useState,useMemo,useCallback
  • 标记副作用(flags),生成更新链表

特点:

  • ✅ 可打断
  • ❌ 不操作 DOM
  • ❌ 不执行副作用
Plain 复制代码
beginWork
  ↓
completeWork

2️⃣ Commit 阶段(提交阶段 / 实际 DOM 操作)

分三步:

  1. before mutation(提交前)

执行:

  • getSnapshotBeforeUpdate
  • 调整内容前的准备
  1. mutation(变更 DOM)

执行所有 DOM 操作:

  • 插入 DOM
  • 删除 DOM
  • 修改 DOM 属性
  • ref 的更新
  1. layout(布局阶段)

执行 Layout Effects:

  • useLayoutEffect 回调
  • class 组件的 componentDidMount / componentDidUpdate

Mutation 和 Layout 这两步是同步执行的,不允许中断。

总结下,上述做了什么?

  • 更新 DOM
  • 执行 useEffect / useLayoutEffect(componentDidMount)
  • ref 赋值

特点:

  • ❌ 不可中断
  • ✅ 必须一次完成

Render 阶段算账,Commit 阶段交付

Plain 复制代码
state 更新 / props 更新 / context 更新
        ↓
---- Render 阶段(可中断) ----
1. 执行组件函数
2. 运行 hooks
3. 生成新的 Fiber 树
4. 计算 diff
5. 构建 effectList(DOM 变更清单)
        ↓
---- Commit 阶段(不可中断) ----
1. before mutation(DOM 操作前的准备)
2. mutation(真实 DOM 更新)
3. layout(执行 useLayoutEffect、componentDidMount)
        ↓
浏览器绘制(paint)
        ↓
最后执行 useEffect(异步)

五、Fiber 是如何实现"可中断"的?

核心思想:时间切片(Time Slicing)

React 不再:

JavaScript 复制代码
renderTree(); // 一口气干完

而是:

JavaScript 复制代码
while (还有时间 && 还有任务) {
  执行一个 Fiber
}

如果:

  • 浏览器要绘制
  • 用户输入
  • 高优先级任务来了

那么,React 暂停当前 Fiber,稍后恢复。

Fiber 为什么能恢复?

因为 Fiber 本身就保存了:

  • 执行到哪了
  • 子节点处理到哪
  • 副作用信息

六、优先级调度(Scheduler)

不同更新优先级不同:

React 可以:

  • 丢弃低优先级渲染
  • 重做高优先级渲染
  • 但 UI 不会乱

七、一次 setState 的 Fiber 流程

JavaScript 复制代码
setCount(c => c + 1);

内部流程:

  1. 创建 update
  2. 挂到 Fiber.updateQueue
  3. 根据优先级调度
  4. 创建 workInProgress Fiber
  5. render 阶段计算新 state(state 永远只在 Fiber 上变)
  6. 标记 flags
  7. commit 阶段更新 DOM

八、和 Vue 响应式的根本差异

Vue:数据驱动更新 React:调度驱动更新

Vue:

JavaScript 复制代码
state.x = 1 → 精确更新

React:

JavaScript 复制代码
setState → 重新执行组件

Fiber 的存在,是 React 能做到并发的前提。

九、如何实现"并发"?

React 并发(Concurrent)不是多线程,而是:在单线程 JS 中,把一次渲染拆成可中断、可重试、可丢弃的任务调度模型。

⚠️ React 并发 ≠ Promise ≠ async/await ≠ Web Worker

并发的是 "渲染任务的调度权",React 可以在多个"未完成的渲染版本"之间来回切换。

本质上就是通过 双 Fiber 树 + render/commit 分离 + scheduler 调度器 ​实现的。

并发特性​是否生效​,取决于:

  • useTransition
  • useDeferredValue
  • Suspense
  • 更新优先级

scheduler 调度器 体现在开发层面是指 useTransition、useDeferredValue、Suspense 等。

这些都是并发

1️⃣ 可中断(Interruptible)
Plain 复制代码
渲染到一半 →
  浏览器要绘制 →
    React 暂停 render
2️⃣ 可重试(Restartable)
Plain 复制代码
低优先级渲染中 →
  高优先级更新来了 →
    丢弃当前渲染 →
      从头再算
3️⃣ 可丢弃(Discardable)
Plain 复制代码
用户连续输入 →
  前面几次渲染直接废弃

最终只 commit"最后一次正确结果"

相关推荐
oh,huoyuyan2 小时前
【实用技巧】火语言RPA:界面『日期时间』控件,实现网页日期自动填写
前端·javascript·rpa
程序员小寒2 小时前
前端性能优化之Webpack篇
前端·webpack·性能优化
我是华为OD~HR~栗栗呀2 小时前
(华为od)21届-Python面经
java·前端·c++·python·华为od·华为·面试
刘一说2 小时前
ES6+核心特性全面浅析
java·前端·es6
kirinlau2 小时前
Vue.observable实现vue原生轻量级状态管理详解
前端·javascript·vue.js
严文文-Chris2 小时前
RAG关键技术要点详解
java·服务器·前端
自然 醒2 小时前
elementUI的select下拉框如何下拉加载数据?
前端·javascript·elementui
我没想到原来他们都是一堆坏人2 小时前
常用npm源与nrm
前端·npm·node.js
编代码的小王2 小时前
【无标题】
前端·css