Fiber 是 React 16 引入的核心调度机制,它本质上是一个 JavaScript 对象(JS Object),但比普通的虚拟 DOM 多了很多关键属性,比如:child、return、sibling、effectTag、priority、expirationTime,用这些属性来支持 增量渲染(分片更新)、任务优先级调度、可中断/恢复渲染 这些高级功能。
Fiber 和普通虚拟 DOM 的区别
| 特性 | 普通虚拟 DOM(旧版 React) | Fiber 节点(React 16+) |
|---|---|---|
| 本质 | 一个 JS 对象,描述 UI 结构 | 也是一个 JS 对象,但额外增加了 调度、任务管理 相关的属性 |
| 作用 | 只是描述 UI 应该长什么样 | 不仅描述 UI,还管理 如何更新、何时更新、更新优先级 |
| 结构 | 简单的树状结构(递归遍历) | 链表结构(child、return、sibling 指针,支持非递归遍历) |
| 更新方式 | 同步递归(一旦开始就卡住,无法中断) | 异步分片(可暂停、恢复、优先级调度) |
| 关键属性 | type(组件类型)、props(属性)、children(子节点) | 额外增加了: ✅ child(第一个子节点) ✅ return(父节点) ✅ sibling(兄弟节点) ✅ effectTag(标记更新类型:插入/更新/删除) ✅ priority(任务优先级) ✅ expirationTime(过期时间,用于调度) |
源码里FiberNode的结构
function FiberNode(
this: $FlowFixMe,
tag: WorkTag,
pendingProps: mixed,
key: null | string,
mode: TypeOfMode,
) {
// 基本属性
this.tag = tag; // 描述此Fiber的启动模式的值(LegacyRoot = 0; ConcurrentRoot = 1)
this.key = key; // React key
this.elementType = null; // 描述React元素的类型。例如,对于JSX<App />,elementType是App
this.type = null; // 组件类型
this.stateNode = null; // 对于类组件,这是类的实例;对于DOM元素,它是对应的DOM节点。
// Fiber链接
this.return = null; // 指向父Fiber
this.child = null; // 指向第一个子Fiber
this.sibling = null; // 指向其兄弟Fiber
this.index = 0; // 子Fiber中的索引位置
this.ref = null; // 如果组件上有ref属性,则该属性指向它
this.refCleanup = null; // 如果组件上的ref属性在更新中被删除或更改,此字段会用于追踪需要清理的旧ref
// Props & State
this.pendingProps = pendingProps; // 正在等待处理的新props
this.memoizedProps = null; // 上一次渲染时的props
this.updateQueue = null; // 一个队列,包含了该Fiber上的状态更新和副作用
this.memoizedState = null; // 上一次渲染时的state
this.dependencies = null; // 该Fiber订阅的上下文或其他资源的描述
// 工作模式
this.mode = mode; // 描述Fiber工作模式的标志(例如Concurrent模式、Blocking模式等)。
// Effects
this.flags = NoFlags; // 描述该Fiber发生的副作用的标志(十六进制的标识)
this.subtreeFlags = NoFlags; // 描述该Fiber子树中发生的副作用的标志(十六进制的标识)
this.deletions = null; // 在commit阶段要删除的子Fiber数组
this.lanes = NoLanes; // 与React的并发模式有关的调度概念。
this.childLanes = NoLanes; // 与React的并发模式有关的调度概念。
this.alternate = null; // Current Tree和Work-in-progress (WIP) Tree的互相指向对方tree里的对应单元
// 如果启用了性能分析
if (enableProfilerTimer) {
// ……
}
// 开发模式中
if (__DEV__) {
// ……
}
}
Fiber 是怎么工作的?
-
第一步:生成新的虚拟 DOM(JSX → Virtual DOM)
- 当用户交互(如点击、输入)发生时,React 会先生成新的 虚拟 DOM(JS 对象),描述 UI 应该变成什么样子。
-
第二步:对比新旧虚拟 DOM(Diffing Algorithm)
- React 会比较 新的虚拟 DOM 和 旧的虚拟 DOM,找出哪些部分发生了变化(比如某个组件的
props变了,或者某个 DOM 节点被删除了)。
- React 会比较 新的虚拟 DOM 和 旧的虚拟 DOM,找出哪些部分发生了变化(比如某个组件的
-
第三步:生成 Fiber 树(构建 Fiber 节点)
- React 会把 虚拟 DOM 转换成 Fiber 节点(也就是给每个虚拟 DOM 节点加上 调度相关的属性,比如
child、return、sibling、effectTag等)。 - 这个过程叫做 "Reconciliation"(协调),React 会 遍历 Fiber 树,找出哪些 Fiber 节点需要更新(比如新增、修改、删除)。
- React 会把 虚拟 DOM 转换成 Fiber 节点(也就是给每个虚拟 DOM 节点加上 调度相关的属性,比如
-
第四步:调度更新(Scheduler)
- React 16 引入了 调度器(Scheduler),它会根据 优先级(比如用户交互 > 动画 > 数据加载)决定 先更新哪些 Fiber 节点。
- 如果某个更新被中断(比如用户突然滚动页面),React 可以 暂停当前任务,先处理更高优先级的任务,等浏览器空闲了再回来继续。
-
第五步:提交更新(Commit Phase)
- 最后,React 会把所有 标记了
effectTag(如PLACEMENT、UPDATE、DELETION)的 Fiber 节点,批量更新到真实 DOM,这样页面就能高效、流畅地变化了。
- 最后,React 会把所有 标记了
-
双缓冲技术: React在更新时,会根据现有的Fiber树(Current Tree)创建一个新的临时树(Work-in-progress (WIP) Tree),WIP-Tree包含了当前更新受影响的最高节点直至其所有子孙节点。Current Tree是当前显示在页面上的视图,WIP-Tree则是在后台进行更新,WIP-Tree更新完成后会复制其它节点,并最终替换掉Current Tree,成为新的Current Tree。因为React在更新时总是维护了两个Fiber树,所以可以随时进行比较、中断或恢复等操作,而且这种机制让React能够同时具备拥有优秀的渲染性能和UI的稳定性。
-
State 和 Props:
memoizedProps、pendingProps和memoizedState字段让React知道组件的上一个状态和即将应用的状态。通过比较这些值,React可以决定组件是否需要更新,从而避免不必要的渲染,提高性能。 -
副作用的追踪:
flags和subtreeFlags字段标识Fiber及其子树中需要执行的副作用,例如DOM更新、生命周期方法调用等。React会积累这些副作用,然后在Commit阶段一次性执行,从而提高效率。
为什么 Fiber 要这么设计?
- 旧版 React(15 及之前) 的更新是 同步递归的,一旦开始渲染,就会一直执行到完成,如果组件树很大,就会导致页面卡顿(比如输入框输入时卡顿)。
- Fiber 架构(16+) 把渲染过程 拆分成多个小任务,可以 暂停、恢复、优先级调度,让 React 能更智能地管理 UI 更新,避免长时间阻塞主线程,从而提升用户体验。
总结
- Fiber 就是一个 JS 对象,但它比普通虚拟 DOM 多了调度相关的属性(如
child、return、sibling、priority)。 - Fiber 的核心作用 是让 React 能 增量渲染、任务优先级调度、可中断/恢复更新,从而让页面更流畅。
- 整个流程:用户交互 → 生成新虚拟 DOM → 对比新旧 DOM → 生成 Fiber 树 → 调度更新(优先级) → 提交到真实 DOM