React Update Queue 源码全链路解析:从 setState 到 DOM 更新

前言

setStatedispatch 在 React 中看似只是一次简单的状态更新,但在 Fiber 架构的内部,它会触发一整套 Update Queue + 调度器 的协作机制,最终才会完成 DOM 更新。

本文将结合源码变量、关键函数和数据结构,完整拆解 React Update Queue 的内部工作流程。


1. Update Queue 在哪里?

在 Fiber 架构中,每个组件实例对应一个 Fiber 节点 ,这个节点有一个 updateQueue 属性,用来存放等待执行的更新任务。

结构示例(简化版):

yaml 复制代码
fiber.updateQueue = {
  baseState: initialState,      // 本轮更新前的状态
  firstBaseUpdate: null,        // 队列起点
  lastBaseUpdate: null,         // 队列终点
  shared: {
    pending: null,              // 待处理的循环链表(新 update 插这里)
  },
  effects: null                  // 额外副作用
};

特点:

  • 每个 Fiber 节点都有自己的 updateQueue
  • 队列采用 循环链表 存储 update
  • 支持批量合并和不同优先级(lane)

2. 创建 Update 对象

当我们调用:

scss 复制代码
setState(newValue)

或者:

bash 复制代码
dispatch({ type: 'add' })

React 会调用 createUpdate(位于 ReactUpdateQueue.new.js)创建一个 Update 对象:

yaml 复制代码
const update = {
  lane: lane,        // 优先级
  action: action,    // setState 传入的值或函数
  eagerReducer: null,
  eagerState: null,
  next: null         // 链表指针
};

注意

  • lane 是调度优先级的关键
  • action 可能是新值,也可能是 (prevState) => newState

3. 插入 Update 到队列

创建好的 update 会被 enqueueUpdate(位于 ReactUpdateQueue.new.js)放入当前 Fiber 的 updateQueue.shared.pending 中:

ini 复制代码
const pending = queue.shared.pending;
if (pending === null) {
  update.next = update; // 第一个节点,自己指向自己
} else {
  update.next = pending.next;
  pending.next = update;
}
queue.shared.pending = update; // 更新尾指针

循环链表的好处:

  • 插入和合并更新都很高效
  • 新 update 可以快速与旧 update 合并

4. 调度更新

队列更新后,React 会调用:

scss 复制代码
scheduleUpdateOnFiber(fiber, lane, eventTime);

这个函数(位于 ReactFiberWorkLoop.new.js)会:

  • 标记当前 Fiber 对应的 FiberRoot 有更新(markRootUpdated
  • 把更新请求交给调度器(Scheduler)

调度器的决定:

  • 同步模式:立即渲染
  • 并发模式:可能延迟,合并更多更新再渲染

5. 渲染阶段处理队列

当 Fiber 进入 Render 阶段

  1. baseState 开始
  2. 遍历 update 链表(processUpdateQueue
  3. 调用 applyUpdate 将每个 update 应用到当前 state
  4. 得到新的 memoizedState

核心伪代码:

ini 复制代码
let newState = baseState;
let update = firstBaseUpdate;
do {
  newState = applyUpdate(update, newState);
  update = update.next;
} while (update !== null);

6. Commit 阶段

Render 阶段算出新 state 后,进入 Commit 阶段:

  1. 更新 Fiber 的 memoizedState
  2. 执行 DOM 更新
  3. 执行副作用(useEffect 等)
  4. 完成渲染

7. 源码变量标注版流程图

scss 复制代码
┌───────────────────────────────┐
│  setState / dispatch(action)  │
│  (action: 值或函数)            │
└──────────────┬────────────────┘
               │
               ▼
     ┌────────────────────────────┐
     │ createUpdate(action, lane) │
     │ ReactUpdateQueue.new.js    │
     │ { lane, action, next: null }│
     └─────────────┬──────────────┘
                   │
                   ▼
┌──────────────────────────────────────────┐
│ enqueueUpdate(fiber.updateQueue, update) │
│ queue = fiber.updateQueue.shared         │
│ pending = queue.pending                  │
│ 插入循环链表 → queue.pending = update     │
└────────────────┬─────────────────────────┘
                 │
                 ▼
       ┌─────────────────────────────────┐
       │ scheduleUpdateOnFiber(fiber, lane) │
       │ ReactFiberWorkLoop.new.js        │
       │ 标记 FiberRoot 脏 → 调度执行      │
       └────────────────┬─────────────────┘
                        │
                        ▼
             ┌────────────────────────────┐
             │ performConcurrentWorkOnRoot │
             │ performSyncWorkOnRoot       │
             │ 根据 lane 决定执行方式      │
             └────────────┬───────────────┘
                          │
                          ▼
         ┌───────────────────────────────────┐
         │ renderRootSync / renderRootConcurrent │
         │ processUpdateQueue(queue)           │
         │ baseState → 遍历 update 链表         │
         │ newState = applyUpdate(update, baseState) │
         │ fiber.memoizedState = newState     │
         └──────────────────┬────────────────┘
                            │
                            ▼
              ┌─────────────────────────────┐
              │ commitRoot(root)             │
              │ 更新 DOM & 执行副作用         │
              │ Fiber.memoizedState ← newState │
              └─────────────────────────────┘

8. 源码关键函数速查

阶段 文件 函数
创建 Update ReactUpdateQueue.new.js createUpdate
插入队列 ReactUpdateQueue.new.js enqueueUpdate
调度更新 ReactFiberWorkLoop.new.js scheduleUpdateOnFiber
处理队列 ReactUpdateQueue.new.js processUpdateQueue
应用更新 ReactUpdateQueue.new.js applyUpdate
渲染执行 ReactFiberWorkLoop.new.js renderRootSync / renderRootConcurrent
提交更新 ReactFiberWorkLoop.new.js commitRoot

总结

  • Update Queue 是 Fiber 节点的更新容器,用循环链表存储 update
  • Update 对象 保存了更新动作和优先级
  • 调度器 根据 lane 决定执行时机
  • processUpdateQueue 渲染阶段批量计算新 state
  • commitRoot 执行实际 DOM 更新和副作用

理解这个机制后,你在调试 React 源码或分析性能瓶颈时,就能快速定位更新的来源、队列状态和调度时机。

相关推荐
井柏然7 分钟前
从 Monorepo 重温 ESM 的模块化机制
前端·javascript·前端工程化
晓得迷路了7 分钟前
栗子前端技术周刊第 102 期 - Vite+ 正式发布、React Native 0.82、Nitro v3 alpha 版...
前端·javascript·vite
XXX-X-XXJ13 分钟前
Vue Router完全指南 —— 从基础配置到权限控制
前端·javascript·vue.js
云和数据.ChenGuang15 分钟前
vue钩子函数调用问题
前端·javascript·vue.js
鹏多多31 分钟前
React动画方案对比:CSS动画和Framer Motion和React Spring
前端·javascript·react.js
亿元程序员34 分钟前
8年游戏主程,一篇文章,多少收益?
前端
IT_陈寒1 小时前
5个Java 21新特性实战技巧,让你的代码性能飙升200%!
前端·人工智能·后端
咖啡の猫1 小时前
Vue内置指令与自定义指令
前端·javascript·vue.js
昔人'1 小时前
使用css `focus-visible` 改善用户体验
前端·css·ux
前端双越老师1 小时前
译: 构建高效 AI Agent 智能体
前端·node.js·agent