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

相关推荐
DanCheOo1 天前
AI 应用的安全架构:Prompt 注入、数据泄露、权限边界
前端·人工智能·prompt·安全架构
We་ct1 天前
深度剖析浏览器跨域问题
开发语言·前端·浏览器·跨域·cors·同源·浏览器跨域
weixin_427771611 天前
前端调试隐藏元素
前端
爱上好庆祝1 天前
学习js的第五天
前端·css·学习·html·css3·js
C澒1 天前
IntelliPro 产研协作平台:基于 AI Agent 的低代码智能化配置方案设计与实现
前端·低代码·ai编程
一袋米扛几楼981 天前
【Git】规范化协作:详解 GitHub 工作流中的 Issue、Branch 与 Pull Request 最佳实践
前端·git·github·issue
网络点点滴1 天前
前端与后端的区别与联系
前端
EnCi Zheng1 天前
M5-markconv自定义CSS样式指南 [特殊字符]
前端·css·python
kyriewen1 天前
你的网页慢,用户不说直接走——前端性能监控教你“读心术”
前端·性能优化·监控
广州华水科技1 天前
北斗GNSS变形监测在大坝安全监测中的应用与优势分析
前端