虚拟 DOM 并不是一个神秘的黑盒,它的本质就是一个用 JavaScript 对象对真实 DOM 的轻量级描述。你可以把它理解为 UI 的“蓝图”:一个嵌套的、包含类型、属性和子节点信息的纯数据结构。因为它是纯粹的 JavaScript 对象,所以创建和操作它的成本远低于直接操作浏览器 DOM 节点。
从 JSX 到虚拟 DOM
当你在 React 中写下 JSX,它会被 Babel 等编译器转译为 React.createElement 调用,最终生成一组 JavaScript 对象。例如:
<div className="container">
<h1>欢迎</h1>
<p>这是一个段落</p>
</div>
编译后大致相当于:
React.createElement(
'div',
{ className: 'container' },
React.createElement('h1', null, '欢迎'),
React.createElement('p', null, '这是一个段落')
)
执行这些 createElement 调用后,会得到一个嵌套的 JavaScript 对象,类似:
{
type: 'div',
props: {
className: 'container',
children: [
{
type: 'h1',
props: { children: '欢迎' }
},
{
type: 'p',
props: { children: '这是一个段落' }
}
]
}
}
这个对象就是虚拟 DOM 树。它的结构非常清晰:
type:节点类型。对于原生 DOM 元素,它是字符串(如'div'、'h1');对于函数/类组件,它是对应的组件函数或类本身。props:节点属性,包括className、style、事件处理器等,以及特殊的children属性(子节点)。children:子节点列表,可以是更多虚拟 DOM 节点、字符串、数字或布尔值。
为什么需要这样的对象描述
1. 创建成本极低
在浏览器中创建一个真实的 <div> 节点,需要调用 document.createElement('div'),这会返回一个重量级的 DOM 元素,上面挂载着数百个属性和方法。而虚拟 DOM 只是一个普通对象,在 JavaScript 引擎中生成和遍历的速度极快。
2. 便于跨平台抽象
虚拟 DOM 描述的是 UI 的结构和内容,不绑定到特定的渲染引擎。React 可以将同一棵虚拟 DOM 树渲染到不同的目标环境:
- 浏览器 DOM(React DOM)
- iOS/Android 原生组件(React Native)
- Canvas(如 react-canvas)
- 终端(如 ink)
这种抽象能力是 React “Learn Once, Write Anywhere”理念的技术基石。
3. 批量更新与 Diff 的基础
每次状态变化时,React 会生成一棵全新的虚拟 DOM 树,然后通过 Diff 算法比较新旧两棵树,计算出最小的更新操作,最后只将这些操作应用到真实 DOM。整个过程都在轻量级的对象层面完成,极大地减少了昂贵 DOM 操作的次数。
虚拟 DOM 不是银弹
需要明确的一点是:虚拟 DOM 并不能让所有场景都变得最快。在极简单的 UI 更新中,直接操作 DOM 可能更快。但虚拟 DOM 的核心价值在于:在中等及高复杂度的应用中,它能自动提供接近最优的批量更新策略,同时让开发者用声明式方式编写 UI,而不必手动优化 DOM。它是开发体验与性能之间的一个绝佳平衡点。
与真实 DOM 的对比
| 特性 | 虚拟 DOM | 真实 DOM |
|------|----------|----------|
| 本质 | 纯 JS 对象 | 浏览器引擎实现的 C++ 对象(在 JS 中表现为 DOM 接口) |
| 体积 | 轻量,只含关键信息 | 重量级,携带大量方法和属性 |
| 创建速度 | 极快,仅涉及对象分配 | 慢,需要调用浏览器 API 并初始化复杂内部状态 |
| 跨平台 | 天然跨平台,可映射到不同渲染器 | 只存在于浏览器环境 |
| 操作成本 | 极低,纯 JS 计算 | 高,每次操作可能触发回流/重绘 |
实际开发中如何看待虚拟 DOM
在日常开发中,你几乎不需要直接创建或操作虚拟 DOM 对象,因为 JSX 编译器已经帮你完成了转换。但理解虚拟 DOM 是什么,能帮助你真正弄懂以下问题:
- 为什么状态更新后并不总是立即反映在页面上?(因为要经过 Diff 和批量更新)
- 为什么元组、列表的
key如此重要?(它帮助 Diff 算法识别节点身份) - 为什么 React 可以渲染到手机原生界面?(因为虚拟 DOM 只描述结构,渲染器决定具体绘制方式)
把虚拟 DOM 看作 UI 的“中间语言”——用极简的 JS 对象表达界面的当前状态,剩下的优化逻辑交给 React 去做。你只需专心书写组件,定义好数据到 UI 的映射关系即可。