Skip to content
 

Fiber 架构

更新: 9/4/2025字数: 0 字 时长: 0 分钟

一、引言

在讲解Fiber架构之前,我们需要知道react中为什么useState钩子函数能够保留最新用户更该的值,(函数式组件中某一个 state 状态的改变,都会触发组件的重新渲染)

那么它是怎么存储更新后的值呢?

因为

在 React 中,useState 能够保留最新的状态值,即使函数组件在每次渲染时都会重新执行,其背后的机制涉及 闭包(Closure)React Fiber 架构 的协作。

1.1、闭包(Closure)的作用

useState 返回的状态值(如 count)和更新函数(如 setCount)通过闭包引用了当前渲染周期的状态快照。每次组件渲染时,useState 会返回 当前最新的状态值(由 React 内部维护)。

更新函数(如 setCount)会将新状态 提交到 React 的调度系统,触发重新渲染。

ts
function Counter() {
  const [count, setCount] = useState(0); // 闭包捕获当前 count 值
  const handleClick = () => {
    setCount(count + 1); // 提交更新,闭包中的 count 是当前渲染周期的值
  };
  return <button onClick={handleClick}>{count}</button>;
}

1.2、React Fiber 的状态存储

React 内部通过 Fiber 节点 管理组件的状态:每个函数组件对应一个 Fiber 节点,状态值存储在 Fiber 节点的 memoizedState 属性中。

组件重新渲染时,useState 会从 Fiber 节点读取最新状态,而不是重新初始化。

Fiber 节点结构示例:

ts
{
  memoizedState: 42, // 当前状态值
  queue: { ... },    // 更新队列(存储 setState 的调用)
  next: null,        // 指向下一个 Hook
}

1.3、状态更新的异步批处理

当调用 setCount 时: 1,React 不会立即修改状态,而是将更新放入 调度队列。 2,在下次渲染时,React 会 合并多个更新(批处理),并计算最终状态。 3,组件重新执行时,useState 会返回 更新后的最新值。

tsx
function Counter() {
  const [count, setCount] = useState(0);
  const handleClick = () => {
    setCount(count + 1); // 第一次更新:基于当前 count=0
    setCount(count + 1); // 第二次更新:仍基于当前 count=0(闭包陷阱!)
  };
  // 点击后 count 只会 +1,而非 +2
  return <button onClick={handleClick}>{count}</button>;
}

二、Fiber 架构讲解

ReactFiber 架构 中,组件的节点关系是通过 链表结构(而非数组或纯对象)管理的;

2.1、Fiber 节点的核心结构

每个 Fiber 节点是一个 JavaScript 对象,包含以下关键属性(简化版):

ts
interface Fiber {
  // 标识组件类型(函数/类组件、Host 组件等)
  tag: WorkTag; // 如 FunctionComponent, ClassComponent, HostComponent

  // 组件相关的实例或函数
  type: any; // 对于函数组件,type 是组件函数本身

  // 状态相关的属性
  memoizedState: any; // 当前状态(hooks 链表存储在这里)
  stateNode: any;     // 类组件的实例或 DOM 节点

  // 链表结构(关键)
  child: Fiber | null;      // 第一个子节点
  sibling: Fiber | null;    // 下一个兄弟节点
  return: Fiber | null;     // 父节点

  // 更新队列和副作用
  updateQueue: UpdateQueue<any> | null; // 状态更新队列
  flags: Flags; // 标记是否需要插入、更新、删除等操作

  // 其他调度相关属性
  alternate: Fiber | null; // 指向当前/WorkInProgress 树的对应节点
}

2.2、多个 Fiber 节点如何组织?

Fiber 节点之间通过 链表(而非数组)形成树状结构:

HTML结构:

html
function App() {
  return (
    <div>
      <Header />
      <Content />
    </div>
  );
}

对应的Fiber 树:

html
        App Fiber
           |
        div Fiber
         /    \
Header Fiber   Content Fiber

2.3、Hooks 的存储方式

1,函数组件的状态(如 useState、useEffect)存储在 Fiber 节点的 memoizedState 属性中,具体是通过 单向链表 组织的: 每个 Hook 是一个对象,包含 memoizedState(如状态值)、next(指向下一个 Hook)。

2,React 通过调用顺序匹配 Hook(因此不能条件式调用 Hook)

Hook 链表示例:

tsx
function Example() {
  const [count, setCount] = useState(0); // Hook 1
  const [name, setName] = useState("");  // Hook 2
  useEffect(() => {});                   // Hook 3
  // ...
}

对应的 Hook 链表:

tsx
Fiber.memoizedState -> Hook1(count) -> Hook2(name) -> Hook3(effect) -> null
                        |                |               |
                     memoizedState    memoizedState    memoizedState

2.4、为什么用链表而非数组?

1.动态性:链表可以高效地插入、删除节点(如条件渲染)。
2.增量渲染:React 可以中断和恢复渲染过程,链表结构更灵活。
3.Hooks 依赖调用顺序:链表能严格保证 Hook 的顺序一致性。

2.5、完整 Fiber 树的遍历过程

React 通过 深度优先遍历 Fiber 树:
1.从根节点开始,访问 child 直到叶子节点。
2.如果没有 child,则访问 sibling。
3.如果没有 sibling,则回溯到 return(父节点)。


那么问题来了?什么是链表结构?js该怎么表示出来?

三、链表相关

3.1、什么是链表结构?

链表(Linked List)是一种 线性数据结构,由一系列节点(Node)组成,每个节点包含:
(1) 数据域(存储数据)
(2) 指针域(指向下一个节点的引用)

3.2、单链表的 JavaScript 实现

ts
// 下面是链表结构
class ListNode {
  constructor(value) {
    this.value = value; // 数据域
    this.next = null;   // 指针域(指向下一个节点)
  }
}

// 创建链表: 1 -> 2 -> 3
const node1 = new ListNode(1);
const node2 = new ListNode(2);
const node3 = new ListNode(3);

node1.next = node2;
node2.next = node3;

// 遍历链表
let current = node1;
console.log("链表内容:",current);
while (current !== null) {
  console.log(current.value); // 依次输出 1, 2, 3
  current = current.next;
}

输出:

ts
// 链表内容:
 ListNode {
  value: 1,
  next: ListNode { value: 2, next: ListNode { value: 3, next: null } }
}

3.3、链表 vs 数组

链表优点:动态大小,插入删除比较快

特性数组链表
内存连续内存非连续内存(动态分配)
查询快(O(1) 索引访问)慢(O(n),需遍历)
插入慢(需移动元素,O(n))快(修改指针,O(1))
删除慢(需移动元素,O(n))快(修改指针,O(1))
优点随机访问快动态大小,插入/删除快

为什么查询比较慢:
1,链表的节点在内存中是非连续存储的(无法像数组直接通过索引访问)。
2,链表的查询操作通常是线性查找,即从链表的头部开始,逐个节点遍历,直到找到目标节点。

总结:
链表:通过指针连接的非连续数据结构,适合动态增删(如 React 的 Fiber 树)。
DFS/BFS:DFS 用栈/递归深入到底,BFS 用队列按层扩展。
React 的应用:Fiber 树的链表结构 + DFS 遍历,实现了可中断的渲染。

我见青山多妩媚,料青山见我应如是。