前言
setState
或 dispatch
在 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 阶段:
- 从
baseState
开始 - 遍历 update 链表(
processUpdateQueue
) - 调用
applyUpdate
将每个 update 应用到当前 state - 得到新的
memoizedState
核心伪代码:
ini
let newState = baseState;
let update = firstBaseUpdate;
do {
newState = applyUpdate(update, newState);
update = update.next;
} while (update !== null);
6. Commit 阶段
Render 阶段算出新 state 后,进入 Commit 阶段:
- 更新 Fiber 的
memoizedState
- 执行 DOM 更新
- 执行副作用(
useEffect
等) - 完成渲染
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 源码或分析性能瓶颈时,就能快速定位更新的来源、队列状态和调度时机。