【react18原理探究实践】分层解析React Fiber 核心工作流程

🧑‍💻 引言

React Fiber 是 React 16 引入的全新协调引擎,它彻底重写了 React 的内部渲染机制,实现了 可中断渲染、优先级调度、时间切片 等核心能力。

本文将以 Fiber 的完整工作流程 为主线,结合源码拆解,分析 React Fiber 从 调度到 commit 的全过程,理解它如何保证 UI 高效、流畅渲染。

核心函数概览

React Fiber 的工作流程涉及以下 10 个核心函数:

  • 调度层performSyncWorkOnRootperformConcurrentWorkOnRoot
  • 渲染层renderRootSyncrenderRootConcurrent
  • 循环层workLoopSyncworkLoopConcurrent
  • 单元层performUnitOfWork
  • 遍历层beginWorkcompleteUnitOfWorkcompleteWork

一、调度层:渲染流程的入口

1.1 performSyncWorkOnRoot - 同步渲染入口

javascript 复制代码
function performSyncWorkOnRoot(root) {
  // 刷新副作用
  flushPassiveEffects();

  // 获取待处理的lanes
  let lanes = getNextLanes(root, NoLanes);

  // 执行同步渲染
  let exitStatus = renderRootSync(root, lanes);

  // 处理渲染结果...
}

核心职责:

  • 作为同步渲染的统一入口点
  • 处理副作用的刷新和清理
  • 协调渲染状态和错误恢复
  • 确保同步渲染的原子性

1.2 performConcurrentWorkOnRoot - 并发渲染入口

javascript 复制代码
function performConcurrentWorkOnRoot(root, didTimeout) {
  // 清除当前事件时间
  currentEventTime = NoTimestamp;
  currentEventTransitionLane = NoLanes;

  // 执行并发渲染
  let exitStatus = renderRootConcurrent(root, lanes);

  // 处理时间切片和优先级调度...
}

核心特点:

  • 支持时间切片的可中断渲染
  • 实现优先级调度机制
  • 处理并发模式下的复杂状态管理

二、渲染层:环境准备与初始化

2.1 renderRootSync - 同步渲染准备

javascript 复制代码
function renderRootSync(root: FiberRoot, lanes: Lanes) {
  // 设置执行上下文
  const prevExecutionContext = executionContext;
  executionContext |= RenderContext;

  // 准备工作进度
  if (workInProgressRoot !== root || workInProgressRootRenderLanes !== lanes) {
    prepareFreshStack(root, lanes);
  }

  // 执行同步工作循环
  do {
    try {
      workLoopSync();
      break;
    } catch (thrownValue) {
      handleError(root, thrownValue);
    }
  } while (true);

  // 恢复执行上下文
  executionContext = prevExecutionContext;
}

2.2 renderRootConcurrent - 并发渲染准备

javascript 复制代码
function renderRootConcurrent(root: FiberRoot, lanes: Lanes) {
  // 类似的上下文设置
  executionContext |= RenderContext;

  // 准备工作进度
  if (workInProgressRoot !== root || workInProgressRootRenderLanes !== lanes) {
    prepareFreshStack(root, lanes);
  }

  // 执行并发工作循环
  do {
    try {
      workLoopConcurrent();
      break;
    } catch (thrownValue) {
      handleError(root, thrownValue);
    }
  } while (true);
}

两者主要区别:

  • 同步模式不可中断,必须一次性完成
  • 并发模式支持时间切片,可以被高优先级任务中断

三、循环层:工作循环的核心逻辑

3.1 workLoopSync - 同步工作循环

javascript 复制代码
function workLoopSync() {
  // 不可中断的循环处理
  while (workInProgress !== null) {
    performUnitOfWork(workInProgress);
  }
}

3.2 workLoopConcurrent - 并发工作循环

javascript 复制代码
function workLoopConcurrent() {
  // 可中断的循环处理
  while (workInProgress !== null && !shouldYield()) {
    performUnitOfWork(workInProgress);
  }
}

关键差异:

  • shouldYield()函数检查是否需要让出执行权
  • 实现了 React 的时间切片机制
  • 支持优先级更高的任务抢占执行

四、单元层:Fiber 节点的统一处理

4.1 performUnitOfWork - 工作单元处理核心

javascript 复制代码
function performUnitOfWork(unitOfWork: Fiber): void {
  const current = unitOfWork.alternate;

  // 执行"递"阶段 - 向下遍历
  let next = beginWork(current, unitOfWork, subtreeRenderLanes);

  // 更新memoized属性
  unitOfWork.memoizedProps = unitOfWork.pendingProps;

  if (next === null) {
    // 没有子节点,开始"归"阶段
    completeUnitOfWork(unitOfWork);
  } else {
    // 继续处理子节点
    workInProgress = next;
  }
}

核心作用:

  • 串联整个深度优先遍历过程
  • 协调"递"和"归"两个阶段
  • 维护 workInProgress 指针的正确流转

五、遍历层:深度优先遍历的具体实现

5.1 beginWork - "递"阶段处理

javascript 复制代码
function beginWork(
  current: Fiber | null,
  workInProgress: Fiber,
  renderLanes: Lanes
): Fiber | null {
  // 根据fiber.tag分发处理逻辑
  switch (workInProgress.tag) {
    case FunctionComponent:
      return updateFunctionComponent(
        current,
        workInProgress,
        Component,
        resolvedProps,
        renderLanes
      );
    case ClassComponent:
      return updateClassComponent(
        current,
        workInProgress,
        Component,
        resolvedProps,
        renderLanes
      );
    case HostComponent:
      return updateHostComponent(current, workInProgress, renderLanes);
    // ... 其他组件类型
  }
}

主要职责:

  • 根据 Fiber 节点类型执行相应的更新逻辑
  • 处理 props 和 state 的变化
  • 创建或复用子 Fiber 节点
  • 通过 bailout 策略优化性能

5.2 completeUnitOfWork - "归"阶段协调

javascript 复制代码
function completeUnitOfWork(unitOfWork: Fiber): void {
  let completedWork = unitOfWork;

  do {
    const current = completedWork.alternate;
    const returnFiber = completedWork.return;

    // 调用completeWork处理当前节点
    let next = completeWork(current, completedWork, subtreeRenderLanes);

    if (next !== null) {
      // 有新的工作产生,继续处理
      workInProgress = next;
      return;
    }

    // 处理兄弟节点
    const siblingFiber = completedWork.sibling;
    if (siblingFiber !== null) {
      workInProgress = siblingFiber;
      return;
    }

    // 回到父节点
    completedWork = completedWork.return;
    workInProgress = completedWork;
  } while (completedWork !== null);

  // 工作循环完成
  workInProgressRootExitStatus = RootCompleted;
}

5.3 completeWork - "归"阶段具体处理

javascript 复制代码
function completeWork(
  current: Fiber | null,
  workInProgress: Fiber,
  renderLanes: Lanes
): Fiber | null {
  const newProps = workInProgress.pendingProps;

  switch (workInProgress.tag) {
    case HostComponent: {
      // 处理DOM元素
      if (current !== null && workInProgress.stateNode != null) {
        // 更新现有元素
        updateHostComponent(
          current,
          workInProgress,
          type,
          newProps,
          rootContainerInstance
        );
      } else {
        // 创建新元素
        const instance = createInstance(
          type,
          newProps,
          rootContainerInstance,
          currentHostContext,
          workInProgress
        );
        appendAllChildren(instance, workInProgress, false, false);
        workInProgress.stateNode = instance;
      }
      break;
    }
    case HostText: {
      // 处理文本节点
      const newText = newProps;
      if (current && workInProgress.stateNode != null) {
        updateHostText(current, workInProgress, oldText, newText);
      } else {
        workInProgress.stateNode = createTextInstance(
          newText,
          rootContainerInstance,
          currentHostContext,
          workInProgress
        );
      }
      break;
    }
    // ... 其他节点类型处理
  }

  // 冒泡属性到父节点
  bubbleProperties(workInProgress);
  return null;
}

主要功能:

  • 根据 Fiber 类型处理 DOM 创建/更新
  • 收集副作用标记
  • 冒泡子节点的 lanes 和 flags
  • 构建完整的副作用链

六、完整工作流程图

scss 复制代码
用户触发更新 (setState, props变化等)
         ↓
    调度器决定渲染模式
         ↓
performSyncWorkOnRoot ← → performConcurrentWorkOnRoot
         ↓                        ↓
   renderRootSync        ←  →  renderRootConcurrent
         ↓                        ↓
    workLoopSync         ←  →  workLoopConcurrent
         ↓                        ↓
         performUnitOfWork (循环处理每个Fiber节点)
              ↓
         beginWork (递阶段)
              ↓
    返回子节点 ← → 返回null
         ↓           ↓
   继续处理子节点    completeUnitOfWork (归阶段)
         ↓               ↓
         ↓          completeWork
         ↓               ↓
         ↓      处理兄弟节点或返回父节点
         ↓               ↓
         ←───────────────┘
              ↓
    整个Fiber树遍历完成
              ↓
      进入commit阶段处理副作用
              ↓
         DOM更新完成

七、核心设计原理

7.1 双缓存架构

React Fiber 使用双缓存架构,维护两棵 Fiber 树:

  • current 树:当前页面显示的 UI 对应的 Fiber 树
  • workInProgress 树:正在内存中构建的 Fiber 树

两棵树通过alternate属性相互引用,完成渲染后进行角色互换。

7.2 深度优先遍历

Fiber 树的遍历采用深度优先策略,分为两个阶段:

  • 递阶段 :从根节点向叶子节点遍历,调用beginWork
  • 归阶段 :从叶子节点向根节点回溯,调用completeWork

7.3 可中断渲染

并发模式下,React 通过以下机制实现可中断渲染:

  • 时间切片 :通过shouldYield()检查剩余时间
  • 优先级调度:使用 lanes 模型管理任务优先级
  • 工作暂停与恢复:保存工作进度,支持任务的暂停和恢复

7.4 副作用收集

在遍历过程中,React 收集所有需要执行的副作用:

  • Placement:插入新节点
  • Update:更新现有节点
  • Deletion:删除节点
  • Passive:异步副作用(useEffect 等)

八、性能优化策略

8.1 Bailout 优化

当组件的 props 和 state 没有变化时,React 会跳过该组件及其子树的处理:

javascript 复制代码
// 在beginWork中的bailout检查
if (
  current !== null &&
  oldProps === newProps &&
  !hasLegacyContextChanged() &&
  !includesSomeLane(renderLanes, updateLanes)
) {
  return bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes);
}

8.2 lanes 优先级模型

React 使用 lanes 模型管理任务优先级:

  • SyncLane:同步任务,最高优先级
  • DefaultLane:默认优先级
  • TransitionLane:过渡动画优先级
  • RetryLane:重试任务优先级

8.3 时间切片机制

javascript 复制代码
function shouldYield() {
  return getCurrentTime() >= deadline;
}

通过检查当前时间是否超过时间片截止时间,决定是否让出执行权。

参考资料

相关推荐
IT_陈寒2 小时前
「JavaScript 性能优化:10个让V8引擎疯狂提速的编码技巧」
前端·人工智能·后端
ObjectX前端实验室3 小时前
【react18原理探究实践】scheduler原理之Task 完整生命周期解析
前端·react.js
ObjectX前端实验室3 小时前
【react18原理探究实践】调度器(Scheduler)原理深度解析
前端·react.js
路漫漫心远3 小时前
音视频学习笔记十八——图像处理之OpenCV检测
前端
摸着石头过河的石头3 小时前
从零开始玩转前端:一站式掌握Web开发基础知识
前端·javascript
sniper_fandc4 小时前
关于Mybatis-Plus的insertOrUpdate()方法使用时的问题与解决—数值精度转化问题
java·前端·数据库·mybatisplus·主键id
10岁的博客4 小时前
技术博客SEO优化全攻略
前端
南屿im5 小时前
别再被引用坑了!JavaScript 深浅拷贝全攻略
前端·javascript
想要一辆洒水车5 小时前
vuex4源码分析学习
前端