Skip to main content

批处理

React 的**批处理(Batching)**是一种性能优化机制,它将多次状态更新合并为一次渲染,从而减少不必要的 DOM 操作。以下从底层原理、版本差异和实现细节三个方面详细解析:

1. 批处理的核心原理

合并多次状态更新

React 会收集多个状态更新(如 setStateuseState 的调用),然后批量执行一次渲染,而非每次调用都触发渲染。这类似于浏览器的事件循环机制。

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>;
}

工作流程

  1. 收集更新:调用 setStateuseState 时,React 将更新加入队列,而非立即应用。
  2. 批量处理:在当前事件处理函数结束后,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)

每次调用 setStateuseState 时,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
底层机制基于更新队列并发渲染 + 优先级调度
性能部分优化全面优化,减少不必要渲染