2-5 React核心概念(三):渲染与高性能原因
从协调到渲染
前两节我们梳理了协调(Reconciliation)的工作流程:虚拟 DOM 的产生、新旧虚拟 DOM 的 Diff 对比。Diff 算法将时间复杂度从 O(n³) 降到了 O(n),这是 JavaScript 层面的优化。但 Diff 只是计算出了"哪些地方需要更新",真正将这些差异同步到真实 DOM 的过程,就是渲染(Rendering)。
React 的渲染器架构
React 的设计者做了一个非常前瞻性的架构决策——将 React 核心库与渲染器解耦。这意味着 React 本身只负责"计算应该怎么更新",而"怎么把更新应用到目标平台"则由不同的渲染器来完成:
| 渲染器 | 目标平台 | 包名 |
|---|---|---|
| React DOM | Web 浏览器 | react-dom |
| React Native | iOS / Android 原生应用 | react-native |
| React Test Utils | 测试环境 | react-dom/test-utils |
| 自定义渲染器 | 任意目标(如 Canvas、终端) | 社区实现 |
这种解耦带来的好处:
- Learn Once, Write Anywhere——同一套 React 组件逻辑可以渲染到不同平台
- 可扩展性——社区可以开发自定义渲染器(如 ink 用于终端 UI、react-three-fiber 用于 3D 渲染)
- 关注点分离——React 核心库专注于状态管理和协调逻辑,渲染器专注于平台适配
setState:触发渲染的核心机制
React 与渲染器之间的通信主要通过 setState 来完成。整个流程如下:
用户交互 / 异步回调
↓
调用 setState({ key: newValue })
↓
React 将更新加入队列
↓
协调阶段:Diff 新旧虚拟 DOM
↓
渲染阶段:将差异同步到真实 DOM
text
在源码层面,当调用 setState 时:
- React 创建一个 update 对象
- 将 update 加入当前组件的更新队列
- 调度器(Scheduler)根据优先级安排更新时机
- 协调器(Reconciler)计算差异
- 渲染器(Renderer)执行实际的 DOM 操作
// 类组件中的 setState
class Counter extends React.Component {
state = { count: 0 };
handleClick = () => {
this.setState({ count: this.state.count + 1 });
// React 内部:创建 update 对象 → 加入队列 → 调度更新
};
render() {
return <button onClick={this.handleClick}>{this.state.count}</button>;
}
}
jsx
Hooks 中的更新机制
Hooks 中的状态更新机制与 setState 非常类似,区别在于它使用 dispatch 而非 setState:
// 函数组件中的 useState
function Counter() {
const [count, setCount] = useState(0);
// setCount 内部实现:
// 创建 update → dispatch 到队列 → 调度更新
return <button onClick={() => setCount(count + 1)}>{count}</button>;
}
jsx
useState 返回的 setter 函数(如 setCount)本质上就是一个 dispatch 函数,它触发的更新流程与 setState 完全一致。
React 高性能的根本原因
React 高性能的秘密不在于某一项单独的技术,而是多个层次的优化协同工作:
第一层:虚拟 DOM
将真实 DOM 操作转化为 JavaScript 对象操作。JavaScript 的执行速度远快于浏览器的 DOM 操作,在内存中完成所有计算后再一次性更新真实 DOM。
第二层:Diff 算法
通过 O(n) 复杂度的 Diff 算法,从新旧虚拟 DOM 中计算出最小差异集,避免全量更新。配合 key 属性实现精准的列表更新。
第三层:批量更新(Batching)
React 会将多个 setState 调用合并为一次更新:
function handleClick() {
setCount(c => c + 1); // 不会立即触发渲染
setFlag(f => !f); // 不会立即触发渲染
setName('New Name'); // 不会立即触发渲染
// 三次 setState 合并为一次渲染
}
jsx
React 18 进一步增强了批处理能力——即使在 setTimeout、Promise、原生事件处理函数中的 setState 也会被自动批处理。
第四层:渲染器解耦
React 核心库不直接操作 DOM,而是通过渲染器接口进行抽象。这意味着不同平台可以使用最优的渲染策略。
React 核心概念回顾
至此,React 的核心概念框架已经清晰:
React 核心机制
├── 协调(Reconciliation)
│ ├── 虚拟 DOM —— JavaScript 对象描述 UI 结构
│ ├── Diff 算法 —— O(n) 复杂度计算最小差异
│ └── key 属性 —— 精准标识列表元素
│
└── 渲染(Rendering)
├── 渲染器架构 —— 核心库与渲染器解耦
├── setState / dispatch —— 触发更新的统一入口
├── 更新队列 —— 批量合并多次更新
└── 多平台支持 —— DOM / Native / 自定义
text
下一节将对比 React 15 和 React 16 在渲染架构上的根本性差异——从 Stack Reconciler 到 Fiber Reconciler 的演进,这是 React 性能飞跃的关键转折点。
↑