前置知识: React

Fiber架构

4 minAdvanced2026/6/14

React Fiber协调引擎

概述

Fiber 是 React 16 引入的全新协调引擎,替代了原有的 Stack Reconciler。Fiber 的核心目标是实现可中断的异步渲染:将渲染工作拆分为小的工作单元(Fiber 节点),在每次处理完一个单元后检查是否需要让出主线程,从而避免长时间阻塞用户交互。Fiber 架构是 React 并发模式、Suspense 和服务端流式渲染的基础。

基础概念

Fiber 节点结构

每个 React 元素对应一个 Fiber 节点,Fiber 节点通过链表结构组织:

Fiber 节点结构:
{
  type,        // 组件类型(函数/类/标签名)
  key,         // 列表中的唯一标识
  props,       // 属性对象
  stateNode,   // 关联的实例或 DOM 节点
  return,      // 父 Fiber 节点
  child,       // 第一个子 Fiber 节点
  sibling,     // 下一个兄弟 Fiber 节点
  alternate,   // 双缓冲对应的 Fiber 节点
  effectTag,   // 副作用标记(插入/更新/删除)
  flags,       // 副作用标志位
  lanes,       // 优先级车道
}

Fiber 树的结构

Fiber 节点通过 child、sibling 和 return 指针形成树结构:

        App (Fiber)
       /           \
  Header          Main
                  /    \
            Sidebar  Content
  • child 指向第一个子节点
  • sibling 指向下一个兄弟节点
  • return 指向父节点

快速上手

Fiber 的工作流程

React 的渲染分为两个阶段:

  1. Render 阶段(可中断):遍历 Fiber 树,计算变更,构建 workInProgress 树
  2. Commit 阶段(不可中断):将变更应用到 DOM
Render 阶段(可中断):
  处理 Fiber 节点 → 检查是否需要让出 → 继续或中断

Commit 阶段(不可中断):
  BeforeMutation → Mutation → LayoutEffect

双缓冲机制

React 使用两棵 Fiber 树实现双缓冲:

  • current 树:当前屏幕上显示的内容
  • workInProgress 树:正在构建的新树

两棵树通过 alternate 指针互指。当 workInProgress 树构建完成后,React 交换根指针,workInProgress 变为 current。

详细用法

工作循环详解

// 简化的 Fiber 工作循环
function workLoop() {
  // 是否需要让出主线程
  while (nextUnitOfWork && !shouldYield()) {
    // 处理一个工作单元
    nextUnitOfWork = performUnitOfWork(nextUnitOfWork);
  }

  if (nextUnitOfWork) {
    // 还有未完成的工作,请求下次空闲时继续
    requestIdleCallback(workLoop);
  } else {
    // 所有工作完成,提交变更
    commitRoot();
  }
}

// 处理单个 Fiber 节点
function performUnitOfWork(fiber) {
  // 1. 处理当前节点(beginWork)
  const children = reconcileChildren(fiber);

  // 2. 优先处理子节点(深度优先)
  if (fiber.child) {
    return fiber.child;
  }

  // 3. 没有子节点,处理兄弟节点
  let nextFiber = fiber;
  while (nextFiber) {
    // 完成当前节点的工作(completeWork)
    completeWork(nextFiber);

    if (nextFiber.sibling) {
      return nextFiber.sibling;
    }
    // 回到父节点继续处理
    nextFiber = nextFiber.return;
  }
}

优先级调度(Lanes 模型)

React 使用 Lanes 模型管理更新优先级:

Lane优先级说明
SyncLane最高同步更新,如 flushSync
InputContinuousLane连续输入,如拖拽
DefaultLane普通默认状态更新
TransitionLane过渡更新,如 useTransition
IdleLane最低空闲时执行
// 优先级调度示例
function ensureRootIsScheduled(root) {
  // 获取最高优先级的待处理更新
  const nextLanes = getNextLanes(root);

  if (nextLanes === NoLanes) {
    // 没有待处理的更新
    return;
  }

  // 根据优先级调度回调
  const newCallbackPriority = getHighestPriorityLane(nextLanes);

  if (newCallbackPriority === SyncLane) {
    // 同步优先级:立即执行
    scheduleSyncCallback(performSyncWorkOnRoot.bind(null, root));
  } else {
    // 其他优先级:调度到空闲时执行
    scheduleCallback(priorityLevel, performConcurrentWorkOnRoot.bind(null, root));
  }
}

Reconciliation 协调过程

// 简化的子节点协调算法
function reconcileChildren(fiber, elements) {
  let index = 0;
  let oldFiber = fiber.alternate?.child;
  let prevSibling = null;

  while (index < elements.length || oldFiber != null) {
    const element = elements[index];
    const sameType = oldFiber && element && element.type === oldFiber.type;

    if (sameType) {
      // 类型相同:更新属性
      const newFiber = {
        type: oldFiber.type,
        props: element.props,
        return: fiber,
        alternate: oldFiber,
        effectTag: 'UPDATE',
      };
    } else if (element && !sameType) {
      // 新元素:插入
      const newFiber = {
        type: element.type,
        props: element.props,
        return: fiber,
        effectTag: 'PLACEMENT',
      };
    } else if (oldFiber && !sameType) {
      // 旧元素不存在于新列表:删除
      oldFiber.effectTag = 'DELETION';
      deletions.push(oldFiber);
    }

    index++;
    oldFiber = oldFiber?.sibling;
  }
}

常见场景

理解 key 的作用

// key 帮助 Fiber 识别哪些元素可以复用
function List({ items }) {
  return (
    <ul>
      {items.map((item) => (
        // key 让 Fiber 知道同一 key 的节点可以复用
        <li key={item.id}>{item.name}</li>
      ))}
    </ul>
  );
}

// 错误用法:使用索引作为 key
// 当列表顺序变化时,Fiber 无法正确复用节点,导致不必要的 DOM 操作
// <li key={index}>{item.name}</li>

理解 useEffect 的执行时机

// useEffect 在 Commit 阶段的 LayoutEffect 之后异步执行
function Component() {
  useEffect(() => {
    // 在 DOM 更新完成后异步调用
    // 不阻塞浏览器绘制
    console.log('副作用执行');
    return () => {
      console.log('清理函数执行');
    };
  }, []);

  useLayoutEffect(() => {
    // 在 DOM 更新后同步调用
    // 阻塞浏览器绘制
    console.log('布局副作用执行');
  }, []);
}

注意事项

  • Fiber 的 Render 阶段可能执行多次(中断后重新开始),不应在 Render 阶段产生副作用
  • Commit 阶段不可中断,应避免在此阶段执行耗时操作
  • key 的稳定性很重要,不要使用随机值或索引作为 key
  • Fiber 架构的内部实现细节可能随版本变化,开发者应关注公开 API 而非内部实现
  • React DevTools 的 Profiler 面板可以可视化 Fiber 树的渲染过程

进阶用法

自定义调度器

// 使用 Scheduler API 控制任务优先级
import { scheduleCallback, NormalPriority } from 'scheduler';

function scheduleCustomTask(callback) {
  scheduleCallback(NormalPriority, () => {
    // 在正常优先级下执行任务
    const result = callback();
    return result;
  });
}

Fiber 与并发模式的关系

// 并发模式依赖 Fiber 的可中断渲染能力
// useTransition 标记的更新会被分配较低的 Lane 优先级
// 高优先级更新(如用户输入)可以中断低优先级渲染

function SearchPage() {
  const [isPending, startTransition] = useTransition();
  const [query, setQuery] = useState('');

  function handleInput(e) {
    // 紧急更新:高优先级 Lane
    setQuery(e.target.value);

    // 过渡更新:低优先级 Lane,可被中断
    startTransition(() => {
      setSearchResults(search(e.target.value));
    });
  }
}

调试 Fiber 树

// 在 React DevTools 中查看 Fiber 节点
// 或通过 __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED 访问

// 获取组件对应的 Fiber 节点(仅调试用)
function getFiberFromDOM(domElement) {
  const key = Object.keys(domElement).find((k) => k.startsWith('__reactFiber$'));
  return domElement[key];
}