实用指南:Fiber 结构和普通 VNode 区别

Fiber 结构和普通 VNode 区别

Date: February 10, 2025
Area: 原理
Question: Fiber 结构和普通 VNode 区别
Review: 25/11/27

本质差异

普通 VNode

UI结构的静态快照,一个轻量的 JavaScript 对象,描述了真实 DOM 应该是什么样子(标签名、属性、子节点等)。它的核心目的是通过 Diff 算法对比新旧 VNode 树,计算出最小变更,最后统一更新真实 DOM,以减少性能开销。

Fiber Node

React 内部的工作单元。Fiber 架构将复杂的渲染更新过程拆分成多个小任务单元(每个 Fiber 节点就是一个单元),并赋予它们不同的优先级。这使得 React 可以在浏览器空闲时执行这些任务,高优先级的更新(如用户输入)可以中断低优先级的更新(如渲染大数据列表),从而实现增量渲染,极大提升应用的响应能力

特性维度普通 VNode (Virtual DOM Node)React Fiber Node
本质目的UI 的静态描述,描述“是什么”动态的工作单元,描述“如何做”
数据结构通常为嵌套的树形结构 (通过 children属性连接)链表树 (通过 childsiblingreturn指针连接)
更新过程同步递归,不可中断,一旦开始直到整棵树 Diff 完成可中断的异步增量更新,将任务拆分成小单元,可暂停、继续和优先级调度
数据存储主要存储与渲染相关的信息 (类型、属性、子节点)存储大量状态、副作用、优先级等信息,是承载组件状态和更新逻辑的实体
与实例关系与组件实例或 DOM 元素无强关联通过 stateNode指向类组件实例、DOM 节点或其他 React 元素类型
生命周期短暂,每次渲染都会重新创建,旧树可被垃圾回收持久化,在多次渲染间复用,通过 alternate指针连接当前树和 workInProgress 树
核心优化通过 Diff 算法减少对真实 DOM 的直接操作时间切片 (Time Slicing)优先级调度,避免阻塞主线程

数据结构对比

普通 VNode(React 15 及之前)

通常是树形结构。父节点通过 children属性引用子节点数组,形成一个嵌套的树。这种结构遍历时通常需要递归,一旦开始难以中断。

const vNode = {
  type: 'div', // 节点类型(组件/原生标签)
  props: { className: 'container' }, // 属性
  children: [vNode1, vNode2], // 子节点(树形结构)
  key: 'unique-id', // 优化 Diff 性能
  // 无状态、调度、副作用信息
}
  • 核心字段:仅包含 UI 描述相关属性(type、props、children)。

Fiber Node(React 16+)

链表树结构(单链表)。每个 Fiber 节点包含 child(第一个子节点)、sibling(下一个兄弟节点)和 return(父节点)指针。这种结构使得遍历可以不需要递归,只需循环即可,从而实现了可中断的深度优先遍历

Case:

image.png

function App() {
	return (
		
Hello World
) }

Fiber Node 数据结构:

export type Fiber = {
  // --- 组件类型与标识 ---
  tag: WorkTag, // 标记fiber的类型 (函数组件、类组件、宿主组件、Fragment等)[1,2](@ref)
  key: null | string, // 唯一标识,用于协调过程中识别同级节点[1](@ref)
  type: any, // 与fiber关联的函数/类/类型 (对于函数组件是函数本身,对于原生标签是字符串)[1](@ref)
  elementType: any, // 元素类型,用于在协调期间保留身份标识
  stateNode: any, // 对应的真实实例 (DOM节点、类组件实例或FiberRoot)[1,2](@ref)
  // --- Fiber树结构指针 (链表结构) ---
  return: Fiber | null, // 指向父级Fiber节点[2,3](@ref)
  child: Fiber | null, // 指向第一个子Fiber节点[2,3](@ref)
  sibling: Fiber | null, // 指向下一个兄弟Fiber节点[2,3](@ref)
  index: number, // 在同级节点中的索引位置,用于Diff算法
  // --- 数据与状态 ---
  pendingProps: any, // 新的、尚未处理的props[1](@ref)
  memoizedProps: any, // 上一次渲染时使用的props[1](@ref)
  memoizedState: any, // 上一次渲染时使用的状态 (函数组件的hooks链表、类组件的state、HostRoot的state)[4,5](@ref)
  updateQueue: mixed, // 存储状态更新和回调函数的队列[1](@ref)
  // --- 副作用与更新标记 ---
  flags: Flags, // 标记当前Fiber需要执行的副作用类型 (如Placement, Update, Deletion)[3,5](@ref)
  subtreeFlags: Flags, // 标记子树中需要执行的副作用
  deletions: Array | null, // 记录需要被删除的子Fiber节点
  lanes: Lanes, // 优先级车道 (React 17+),表示任务的紧迫程度[1,2](@ref)
  childLanes: Lanes, // 子树中的优先级车道
  // --- 双缓存机制 ---
  alternate: Fiber | null, // 指向当前树或workInProgress树中的对应节点,用于复用和比较[3,5](@ref)
};

核心扩展

  • 调度控制lanes 优先级、任务到期时间。
  • 状态管理:Hooks 链表(函数组件)、类组件状态队列。
  • 副作用追踪effectTag 标记和副作用链表。
  • 遍历结构child/sibling/return 构成双向链表。

协调机制对比

流程VNode(Stack Reconciler)Fiber Reconciler
遍历方式递归遍历(不可中断)循环遍历链表(可中断 + 恢复)
任务调度同步执行,阻塞主线程异步分片,空闲时间执行
优先级控制Lane 模型(31 个优先级车道)
副作用处理统一提交 DOM 更新构建副作用链表,分阶段提交

Fiber 两阶段提交

  1. 协调阶段(可中断):
    • 增量构建 Fiber 树,标记副作用(effectTag)。
    • 通过 requestIdleCallback 或 Scheduler 包分片执行。
  2. 提交阶段(同步不可中断):
    • 遍历副作用链表,执行 DOM 操作和生命周期方法。

双缓存技术

这是 Fiber 架构的一个精妙之处。React 同时维护两棵 Fiber 树:

Current Tree:“真实 UI 对应的 Fiber Tree”,代表当前屏幕上显示的状态。

WorkInProgress Tree:“正在内存中构建的 Fiber Tree”,正在内存中构建的新状态。

两棵树通过 alternate指针相互连接。当 workInProgress树构建完成,在 Commit 阶段渲染后,React 会将 current指针指向它,于是 workInProgress树就变成了新的 current树。这种交换技术能确保更新的连贯性,避免中间状态对用户可见。

image.png


能力扩展示例

a. 支持 Hooks 状态管理

  • Fiber 节点通过 memoizedState 字段存储 Hooks 链表:
// 函数组件的 Hooks 链表
fiberNode.memoizedState = {
  memoizedState: 'state value', // useState 的状态
  next: {
    // 下一个 Hook(如 useEffect)
    memoizedState: { cleanup: fn },
    next: null,
  },
}
  • VNode 无状态管理能力,仅描述 UI。

b. 优先级调度实战

  • 高优先级任务抢占

    // 用户输入触发高优先级更新
    input.addEventListener('input', () => {
      React.startTransition(() => {
        setInputValue(e.target.value) // 低优先级
      })
      // 高优先级更新立即执行
    })
  • VNode 架构无法实现任务中断和优先级插队。

c. 副作用批处理

  • Fiber 通过 effectList 链表收集所有变更,统一提交:

    // 提交阶段遍历 effectList
    let nextEffect = fiberRoot.firstEffect
    while (nextEffect) {
      commitWork(nextEffect)
      nextEffect = nextEffect.nextEffect
    }
  • VNode 架构在 Diff 后直接操作 DOM,无批处理优化。


性能影响对比

场景VNode 架构Fiber 架构
大型组件树渲染主线程阻塞导致掉帧分片渲染,保持 UI 响应
高频更新(如动画)多次渲染合并困难基于优先级合并或跳过中间状态
SSR 水合(Hydration)全量同步处理增量水合,优先交互部分
posted on 2026-01-29 10:18  ljbguanli  阅读(0)  评论(0)    收藏  举报