React Fiber 架构与渲染流程

React 的 Fiber 架构是 React 16 中引入的重大重构,它彻底改变了 React 的渲染机制,为并发特性(如 Concurrent Mode)奠定了基础。

为什么需要 Fiber 架构?

传统 Stack Reconciler 的局限性

在 React 16 之前,React 使用栈协调器(Stack Reconciler),其存在以下问题:

  1. 不可中断的递归遍历:渲染过程是同步、不可中断的
  2. 阻塞主线程:大型组件树会导致界面卡顿
  3. 无法优先处理高优先级更新:所有更新同等对待

Fiber 架构的解决方案

Fiber 架构引入了:

  1. 可中断的渲染过程:将工作分解为小单元
  2. 优先级调度:不同更新有不同的优先级
  3. 并发渲染能力:为 Concurrent Mode 提供基础

Fiber 节点的核心结构

Fiber 是 React 的最小工作单元,每个组件对应一个 Fiber 节点:

javascript 复制代码
// Fiber 节点结构(简化版)
type Fiber = {
  // 标识信息
  tag: WorkTag,           // 组件类型(函数组件、类组件、宿主组件等)
  key: null | string,     // 唯一标识
  type: any,              // 组件函数/类或DOM标签名
  
  // 树结构信息
  return: Fiber | null,   // 父节点
  child: Fiber | null,    // 第一个子节点
  sibling: Fiber | null,  // 下一个兄弟节点
  
  // 状态信息
  pendingProps: any,      // 新的 props
  memoizedProps: any,     // 上一次渲染的 props
  memoizedState: any,     // 上一次渲染的状态(hooks、state等)
  stateNode: any,         // 对应的实例(DOM节点、组件实例)
  
  // 副作用相关
  flags: Flags,           // 需要执行的副作用标记(增、删、更新)
  subtreeFlags: Flags,    // 子树中的副作用标记
  deletions: Fiber[] | null, // 待删除的子节点
  
  // 工作进度相关
  alternate: Fiber | null, // 上一次渲染的fiber节点(用于diff)
  lanes: Lanes,           // 优先级车道
  childLanes: Lanes,      // 子节点的优先级车道
  
  // Hook 相关(函数组件)
  memoizedState: any,     // Hook 链表头
};

Fiber 树的双缓存机制

React 使用双缓存技术来避免渲染过程中的视觉闪烁:

  1. Current Tree:当前屏幕上显示内容对应的 Fiber 树
  2. WorkInProgress Tree:正在构建的新 Fiber 树
javascript 复制代码
// 双缓存工作机制
function updateComponent() {
  // 从当前fiber创建workInProgress fiber
  const current = currentlyRenderingFiber.alternate;
  if (current !== null) {
    // 复用现有的fiber节点
    workInProgress = createWorkInProgress(current, pendingProps);
  } else {
    // 创建新的fiber节点
    workInProgress = createFiberFromTypeAndProps(
      // ...参数
    );
  }
  
  // 处理workInProgress树...
}

完整的渲染流程

React 的渲染过程分为两个主要阶段:

1. Render 阶段(可中断)

Render 阶段是异步、可中断的,负责计算变更:

javascript 复制代码
// Render 阶段工作循环
function workLoop(deadline) {
  let shouldYield = false;
  while (nextUnitOfWork !== null && !shouldYield) {
    // 执行当前工作单元
    nextUnitOfWork = performUnitOfWork(nextUnitOfWork);
    
    // 检查是否需要让出主线程
    shouldYield = deadline.timeRemaining() < 1;
  }
  
  if (nextUnitOfWork !== null) {
    // 还有工作,稍后继续
    requestIdleCallback(workLoop);
  } else {
    // 所有工作完成,进入提交阶段
    commitRoot();
  }
}

performUnitOfWork 的深度优先遍历

javascript 复制代码
function performUnitOfWork(fiber) {
  // 1. 开始工作:创建子fiber节点(调用组件render方法)
  const next = beginWork(fiber);
  
  if (next !== null) {
    return next; // 如果有子节点,返回子节点继续处理
  }
  
  // 2. 没有子节点,完成当前节点工作,转向兄弟节点或父节点
  let current = fiber;
  while (current !== null) {
    // 完成当前节点(生成effect列表等)
    completeWork(current);
    
    if (current.sibling !== null) {
      return current.sibling; // 处理兄弟节点
    }
    current = current.return; // 返回父节点
  }
  
  return null; // 遍历完成
}

beginWork:处理组件更新

javascript 复制代码
function beginWork(fiber) {
  switch (fiber.tag) {
    case FunctionComponent:
      // 处理函数组件
      return updateFunctionComponent(fiber);
    case ClassComponent:
      // 处理类组件
      return updateClassComponent(fiber);
    case HostComponent:
      // 处理DOM元素
      return updateHostComponent(fiber);
    // ... 其他组件类型
  }
}

function updateFunctionComponent(fiber) {
  // 准备Hooks环境
  prepareToUseHooks(fiber);
  
  // 调用组件函数,获取子元素
  const children = fiber.type(fiber.pendingProps);
  
  // 协调子元素
  reconcileChildren(fiber, children);
  
  return fiber.child; // 返回第一个子节点
}

completeWork:完成节点处理

javascript 复制代码
function completeWork(fiber) {
  switch (fiber.tag) {
    case HostComponent:
      // 处理DOM元素的属性更新等
      if (fiber.stateNode !== null) {
        // 更新现有的DOM节点
        updateDOMProperties(fiber.stateNode, fiber.memoizedProps, fiber.pendingProps);
      } else {
        // 创建新的DOM节点
        const instance = createInstance(fiber.type, fiber.pendingProps);
        fiber.stateNode = instance;
      }
      break;
    // ... 其他组件类型
  }
  
  // 收集effect到父节点
  if (fiber.flags !== NoFlags) {
    // 将当前fiber的effect添加到父节点的effect列表中
    let parent = fiber.return;
    while (parent !== null) {
      parent.subtreeFlags |= fiber.flags;
      parent = parent.return;
    }
  }
}

2. Commit 阶段(不可中断)

Commit 阶段是同步、不可中断的,负责将变更应用到DOM:

javascript 复制代码
function commitRoot() {
  // 1. 预处理:调用getSnapshotBeforeUpdate等
  commitBeforeMutationEffects();
  
  // 2. 应用DOM变更
  commitMutationEffects();
  
  // 3. 将workInProgress树切换为current树
  root.current = finishedWork;
  
  // 4. 处理布局effect(如useLayoutEffect)
  commitLayoutEffects();
  
  // 5. 调度被动effect(useEffect)
  schedulePassiveEffects();
}

commitMutationEffects:处理DOM变更

javascript 复制代码
function commitMutationEffects() {
  // 遍历effect列表,执行DOM操作
  while (nextEffect !== null) {
    const flags = nextEffect.flags;
    
    if (flags & Placement) {
      // 插入新节点
      commitPlacement(nextEffect);
    }
    if (flags & Update) {
      // 更新节点
      commitUpdate(nextEffect);
    }
    if (flags & Deletion) {
      // 删除节点
      commitDeletion(nextEffect);
    }
    
    nextEffect = nextEffect.nextEffect;
  }
}

优先级调度机制

Fiber 架构引入了优先级概念,确保高优先级更新优先处理:

javascript 复制代码
// 优先级类型(简化)
const SyncLane = 0b0000000000000000000000000000001; // 同步优先级
const InputContinuousLane = 0b0000000000000000000000000000100; // 连续输入
const DefaultLane = 0b0000000000000000000000000010000; // 默认优先级

// 基于优先级的调度
function scheduleUpdateOnFiber(fiber, lane) {
  // 标记优先级
  fiber.lanes = mergeLanes(fiber.lanes, lane);
  
  // 调度更新
  if (lane === SyncLane) {
    // 同步更新,立即执行
    performSyncWorkOnRoot(root);
  } else {
    // 异步更新,根据优先级调度
    ensureRootIsScheduled(root);
  }
}

并发模式下的工作方式

在并发模式下,React 可以中断低优先级工作来处理高优先级更新:

javascript 复制代码
// 高优先级更新中断低优先级工作
function handleUserInput() {
  // 高优先级更新(用户输入)
  scheduleUpdateOnFiber(root, InputContinuousLane);
  
  // 如果当前有低优先级渲染正在进行...
  // React 会中断它,先处理高优先级更新
}

// 被中断的工作可以稍后重新开始
function resumeInterruptedWork(interruptedFiber) {
  // 从中断的地方继续工作
  nextUnitOfWork = interruptedFiber;
  requestIdleCallback(workLoop);
}

错误处理机制

Fiber 架构改进了错误处理:

javascript 复制代码
function renderRoot() {
  try {
    // 正常的渲染工作
    workLoop();
  } catch (error) {
    // 处理错误,寻找错误边界
    let fiber = nextUnitOfWork;
    while (fiber !== null) {
      if (fiber.tag === ClassComponent && 
          typeof fiber.type.getDerivedStateFromError === 'function') {
        // 找到错误边界组件
        captureError(fiber, error);
        break;
      }
      fiber = fiber.return;
    }
    
    // 重新尝试渲染
    restartRender();
  }
}

性能优化特性

1. 增量渲染

Fiber 将渲染工作分解为小单元,可以分段完成:

javascript 复制代码
// 时间分片示例
function workLoopConcurrent(deadline) {
  while (nextUnitOfWork !== null && deadline.timeRemaining() > 0) {
    nextUnitOfWork = performUnitOfWork(nextUnitOfWork);
  }
  
  if (nextUnitOfWork !== null) {
    // 还有工作,稍后继续
    requestIdleCallback(workLoopConcurrent);
  }
}

2. 子树渲染跳过

当 props 未变化时,可以跳过整个子树的渲染:

javascript 复制代码
function bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes) {
  if (current !== null) {
    // 检查props是否变化
    const oldProps = current.memoizedProps;
    const newProps = workInProgress.pendingProps;
    
    if (oldProps === newProps && !hasContextChanged()) {
      // props未变化,可以跳过整个子树
      return null;
    }
  }
  
  // 需要重新渲染
  return updateFunctionComponent(workInProgress);
}

总结

React Fiber 架构的核心创新:

  1. 可中断的异步渲染:将渲染工作分解为小单元,可以暂停和恢复
  2. 优先级调度:不同更新有不同优先级,确保用户体验流畅
  3. 双缓存机制:避免渲染过程中的视觉闪烁
  4. 增量提交:DOM 变更分批进行,减少布局抖动
  5. 更好的错误处理:完善的错误边界机制

参考: incepter.github.io/how-react-w...

相关推荐
gitboyzcf6 小时前
基于Taro4最新版微信小程序、H5的多端开发简单模板
前端·vue.js·taro
姓王者6 小时前
解决Tauri2.x拖拽事件问题
前端
冲!!6 小时前
vue3存储/获取本地或会话存储,封装存储工具,结合pina使用存储
前端·javascript·vue.js
zzz100666 小时前
Web 与 Nginx 网站服务:从基础到实践
运维·前端·nginx
良木林6 小时前
JS对象进阶
前端·javascript
muyouking117 小时前
一文吃透 CSS 伪类:从「鼠标悬停」到「斑马纹表格」的 30 个实战场景
前端·css
TE-茶叶蛋7 小时前
scss 转为原子css unocss
前端·css·scss
Sapphire~7 小时前
重学前端012 --- 响应式网页设计 CSS变量
前端·css