批处理
React 的**批处理(Batching)**是一种性能优化机制,它将多次状态更新合并为一次渲染,从而减少不必要的 DOM 操作。以下从底层原理、版本差异和实现细节三个方面详细解析:
1. 批处理的核心原理
合并多次状态更新
React 会收集多个状态更新(如 setState
或 useState
的调用),然后批量执行一次渲染,而非每次调用都触发渲染。这类似于浏览器的事件循环机制。
function App() {
const [count, setCount] = useState(0);
const [flag, setFlag] = useState(false);
const handleClick = () => {
setCount(c => c + 1); // 第一次更新
setFlag(f => !f); // 第二次更新
// React 会合并这两次更新,只触发一次渲染
};
return <button onClick={handleClick}>Update</button>;
}
工作流程
- 收集更新:调用
setState
或useState
时,React 将更新加入队列,而非立即应用。 - 批量处理:在当前事件处理函数结束后,React 一次性处理所有队列中的更新,并执行一次渲染。
2. React 17 及以前的批处理
仅在 React 事件处理函数中批处理
- 自动批处理:在 React 事件处理函数(如
onClick
)中,多次状态更新会被自动批处理。 - 非批处理场景:在 setTimeout、Promise、原生事件监听等异步回调中,状态更新不会被批处理。
// React 17 及以前:异步回调中不批处理
setTimeout(() => {
setCount(c => c + 1); // 触发一次渲染
setFlag(f => !f); // 再触发一次渲染
}, 1000);
3. React 18 的自动批处理(Automatic Batching)
批处理范围扩展
React 18 通过 createRoot
启用并发模式后,批处理扩展到所有场景:
- React 事件处理函数
- 异步回调(setTimeout、Promise、原生事件监听等)
- 甚至在
useEffect
中
// React 18 中自动批处理所有场景
setTimeout(() => {
setCount(c => c + 1); // 不触发渲染
setFlag(f => !f); // 不触发渲染
// React 会在 setTimeout 回调结束后,批量执行一次渲染
}, 1000);
手动控制批处理
若需要在 React 18 中强制立即渲染,可以使用 flushSync
:
import { flushSync } from 'react-dom';
const handleClick = () => {
flushSync(() => {
setCount(c => c + 1); // 立即渲染
});
// DOM 已更新
flushSync(() => {
setFlag(f => !f); // 再次立即渲染
});
};
4. 底层实现机制
(1) 更新队列(Update Queue)
每次调用 setState
或 useState
时,React 会创建一个 Update
对象并加入队列:
// 简化的 Update 对象结构
const update = {
action, // 状态更新函数或值
next: null, // 指向下一个 Update 的指针
lane, // 更新优先级(React 18 引入)
};
(2) 渲染阶段(Render Phase)
- 协调(Reconciliation):React 遍历更新队列,计算最终状态。
- 提交(Commit):将最终状态应用到 DOM,并触发组件渲染。
(3) 优先级机制(React 18+)
React 18 引入并发渲染,根据更新的紧急程度分配不同优先级:
- 同步优先级:如用户输入,立即处理。
- 异步优先级:如数据获取,可延迟处理。
5. 性能优化原理
减少渲染次数
批处理通过合并多次状态更新,减少渲染次数,从而提升性能:
- 避免多次 DOM 操作,减少浏览器重排和重绘。
- 减少组件生命周期函数的调用次数。
内存优化
通过队列管理更新,避免创建过多中间状态,降低内存开销。
总结
特性 | React 17 及以前 | React 18+ |
---|---|---|
批处理范围 | 仅 React 事件处理函数 | 所有场景(包括异步回调) |
手动控制 | 无 | flushSync |
底层机制 | 基于更新队列 | 并发渲染 + 优先级调度 |
性能 | 部分优化 | 全面优化,减少不必要渲染 |