Fiber 架构实现原理

Fiber 架构实现原理

Fiber 架构概述

Fiber 是 React 16 引入的全新架构,旨在解决大型应用更新时的性能问题,特别是支持增量渲染和更好的调度控制。

Fiber 的核心设计

  1. 可中断的渲染过程:将渲染工作分割成多个小任务
  2. 优先级调度:不同更新可以有不同的优先级
  3. 增量渲染:将渲染工作分散到多个帧中执行

Fiber 节点结构

Fiber 是对虚拟 DOM 的扩展,每个组件对应一个 Fiber 节点:

function FiberNode(
  tag: WorkTag,
  pendingProps: mixed,
  key: null | string,
  mode: TypeOfMode,
) {
  // 实例相关
  this.tag = tag;           // 组件类型 (函数组件/类组件/DOM节点等)
  this.key = key;           // 唯一标识
  this.elementType = null;   // React元素类型
  this.type = null;         // 组件函数/类或DOM标签名
  this.stateNode = null;    // 对应的真实DOM节点或组件实例

  // Fiber树结构相关
  this.return = null;       // 父Fiber
  this.child = null;        // 第一个子Fiber
  this.sibling = null;      // 下一个兄弟Fiber
  this.index = 0;           // 在兄弟中的索引

  // 副作用相关
  this.flags = NoFlags;     // 标记需要进行的操作(插入/更新/删除等)
  this.subtreeFlags = NoFlags; // 子树中的副作用
  this.deletions = null;    // 需要删除的子节点

  // 状态与props
  this.pendingProps = pendingProps; // 新的props
  this.memoizedProps = null; // 上次渲染使用的props
  this.updateQueue = null;   // 状态更新队列
  this.memoizedState = null; // 上次渲染使用的state
  this.dependencies = null;  // contexts, events等依赖

  // 调度相关
  this.alternate = null;     // 用于连接current和workInProgress树
  this.lanes = NoLanes;      // 调度优先级
  this.childLanes = NoLanes; // 子树的调度优先级
}

Fiber 协调(Reconciliation)过程

双缓存技术

React 使用双缓存技术维护两棵 Fiber 树:

  1. current 树:当前屏幕上显示内容对应的 Fiber 树
  2. workInProgress 树:正在构建的新 Fiber 树

协调过程分为两个阶段

1. 渲染/协调阶段 (Render/Reconciliation Phase)
  • 可中断:React 可以根据时间片决定是否继续执行
  • 执行工作
    • 调用组件渲染函数
    • 比较子元素
    • 标记需要进行的 DOM 操作
function performUnitOfWork(fiber) {
  // 开始工作: 创建新的Fiber或复用现有Fiber
  beginWork(fiber);
  
  if (fiber.child) {
    return fiber.child; // 深度优先遍历
  }
  
  // 没有子节点则处理兄弟节点
  let nextFiber = fiber;
  while (nextFiber) {
    completeWork(nextFiber); // 完成当前节点工作
    
    if (nextFiber.sibling) {
      return nextFiber.sibling; // 返回兄弟节点
    }
    nextFiber = nextFiber.return; // 回溯到父节点
  }
}
2. 提交阶段 (Commit Phase)
  • 不可中断:一次性执行所有 DOM 变更
  • 执行工作
    • 执行生命周期方法
    • 更新 refs
    • 执行 DOM 操作
function commitRoot(root) {
  // 第一阶段: 调用getSnapshotBeforeUpdate等生命周期
  commitBeforeMutationEffects();
  
  // 第二阶段: 执行DOM操作
  commitMutationEffects();
  
  // 切换current指针
  root.current = finishedWork;
  
  // 第三阶段: 调用componentDidMount/Update等生命周期
  commitLayoutEffects();
}

协调算法 (Diffing Algorithm)

React 的协调算法基于两个假设优化性能:

  1. 不同类型的元素会产生不同的树
  2. 通过 key 属性标识哪些子元素是稳定的

Diffing 过程

  1. 比较根元素

    • 类型不同:完全卸载旧树,构建新树
    • 类型相同:保留 DOM 节点,仅更新属性
  2. 比较子元素

    • 列表比较:使用 key 匹配新旧子元素
    • 无 key:按索引比较,性能较差
function reconcileChildrenArray(
  returnFiber: Fiber,
  currentFirstChild: Fiber | null,
  newChildren: Array<*>,
  lanes: Lanes,
): Fiber | null {
  let resultingFirstChild: Fiber | null = null;
  let previousNewFiber: Fiber | null = null;
  
  let oldFiber = currentFirstChild;
  let lastPlacedIndex = 0;
  let newIdx = 0;
  let nextOldFiber = null;
  
  // 第一轮遍历: 处理可复用的节点
  for (; oldFiber !== null && newIdx < newChildren.length; newIdx++) {
    if (oldFiber.index > newIdx) {
      nextOldFiber = oldFiber;
      oldFiber = null;
    } else {
      nextOldFiber = oldFiber.sibling;
    }
    
    const newFiber = updateSlot(
      returnFiber,
      oldFiber,
      newChildren[newIdx],
      lanes,
    );
    
    if (newFiber === null) {
      if (oldFiber === null) {
        oldFiber = nextOldFiber;
      }
      break; // key不匹配,跳出循环
    }
    
    // 标记是否需要移动
    lastPlacedIndex = placeChild(newFiber, lastPlacedIndex, newIdx);
    
    // 构建新Fiber链表
    if (previousNewFiber === null) {
      resultingFirstChild = newFiber;
    } else {
      previousNewFiber.sibling = newFiber;
    }
    previousNewFiber = newFiber;
    oldFiber = nextOldFiber;
  }
  
  // 第二轮遍历: 处理剩余节点
  // ...省略后续处理代码...
  
  return resultingFirstChild;
}

优先级调度

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

  1. 同步优先级 (SyncLane):最高优先级,如用户输入
  2. 批量优先级 (DefaultLane):普通更新
  3. 低优先级 (IdleLane):可以延迟的更新
function scheduleUpdateOnFiber(
  fiber: Fiber,
  lane: Lane,
  eventTime: number,
) {
  // 标记更新优先级
  const root = markUpdateLaneFromFiberToRoot(fiber, lane);
  if (root === null) return;
  
  // 根据优先级调度更新
  if (lane === SyncLane) {
    // 同步更新
    performSyncWorkOnRoot(root);
  } else {
    // 异步更新
    ensureRootIsScheduled(root, eventTime);
  }
}

时间切片 (Time Slicing)

React 使用 requestIdleCallbackMessageChannel 模拟实现时间切片:

function workLoopConcurrent() {
  // 在浏览器空闲时执行工作
  while (workInProgress !== null && !shouldYield()) {
    performUnitOfWork(workInProgress);
  }
  
  // 如果还有未完成的工作,稍后继续
  if (workInProgress !== null) {
    scheduleCallback(performConcurrentWorkOnRoot.bind(null, root));
  }
}

总结

Fiber 架构通过以下机制实现了高效的协调更新:

  1. 增量渲染:将渲染工作分割为可中断的小任务
  2. 优先级调度:根据更新类型分配不同优先级
  3. 双缓存技术:避免渲染过程中的视觉不一致
  4. 高效 Diffing 算法:最小化 DOM 操作
  5. 时间切片:合理利用浏览器空闲时间

这种架构使 React 能够处理大型复杂应用的渲染需求,同时保持流畅的用户体验。

你可能感兴趣的:(Fiber 架构实现原理)