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 源码或分析性能瓶颈时,就能快速定位更新的来源、队列状态和调度时机。

相关推荐
猩猩程序员10 分钟前
Go 1.24 全面拥抱 Swiss Table:让内置 map 提速 60% 的秘密
前端
1024小神12 分钟前
vue3 + vite项目,如果在build的时候对代码加密混淆
前端·javascript
轻语呢喃31 分钟前
useRef :掌握 DOM 访问与持久化状态的利器
前端·javascript·react.js
wwy_frontend1 小时前
useState 的 9个常见坑与最佳实践
前端·react.js
w_y_fan1 小时前
flutter_riverpod: ^2.6.1 应用笔记 (一)
前端·flutter
Jerry1 小时前
Compose 界面工具包
前端
Focusbe1 小时前
从0到1开发一个AI助手
前端·人工智能·面试
egghead263161 小时前
React组件通信
前端·react.js
RIKA1 小时前
【前端工具】使用 Node.js 脚本实现项目打包后自动压缩
前端
橙某人1 小时前
🖼️照片展示新境界!等高不等宽自适应布局完整教程⚡⚡⚡
前端·javascript·css