对应源码:ReactFiberConcurrentUpdates.js, ReactFiberRootScheduler.js
1. 什么是批量更新
Automatic Batching 是 React 18 的核心改进之一,将批量更新从 React 事件处理器扩展到了所有场景。
批量更新指 React 将多个 state 更新合并到一次重新渲染中:
javascript
// React 17(不自动批量):
setTimeout(() => {
setCount(c => c + 1); // 渲染 1
setFlag(f => !f); // 渲染 2
}, 0);
// React 18+(自动批量):
setTimeout(() => {
setCount(c => c + 1); // (两次 setState
setFlag(f => !f); // 只渲染 1 次)
}, 0);2. 实现机制
React 18 的批量更新通过"延迟调度"实现(核心逻辑在 ReactFiberRootScheduler.js 的 ensureRootIsScheduled 中):
每次 dispatchSetState:
1. 创建 Update,加入队列
2. markRootUpdated(root, lane) → 更新 root.pendingLanes
3. ensureRootIsScheduled(root)
ensureRootIsScheduled:
检查是否已有同优先级的任务在调度
如果有 → 不创建新任务(合并!)
如果没有 → scheduleCallback 创建新任务
结果:同一事件循环中的多个 setState
→ 第一次调用 ensureRootIsScheduled → 创建任务
→ 后续调用的 ensureRootIsScheduled → 发现已有同优先级任务 → 跳过
→ 一个任务统一处理所有更新3. React 17 vs 18 的区别
React v18.0 博客详细说明了这一改进的动机。
React 17:
React 事件处理器内:自动批量 ✓
setTimeout / Promise / 原生事件:不批量 ✗
原因:只在 React 合成事件中包装了 batching 逻辑
React 18:
所有地方:自动批量 ✓
原因:批量逻辑移到了 ensureRootIsScheduled 中
不依赖事件边界,任何地方的 setState 都会通过同一个调度入口4. flushSync:强制同步刷新
如果需要立即更新(不批量),用 flushSync:
javascript
import { flushSync } from 'react-dom';
flushSync(() => {
setCount(c => c + 1); // 立即渲染
});
setFlag(f => !f); // 之后再渲染下一步
- Offscreen / Activity — Offscreen 组件
- View Transitions — View Transitions
- Suspense 机制 — Suspense 的实现机制