在学习 React 的过程中,虚拟 DOM(Virtual DOM) 是绕不过去的核心概念。
很多面试也会经常问:
"什么是虚拟 DOM?它解决了什么问题?有什么优点?"
然而,很多人停留在一句:
虚拟 DOM 就是用 JavaScript 对象来描述真实 DOM 结构。
这种说法虽然没错,但过于表面,容易把虚拟 DOM
和 React
的实现细节混为一谈。
今天我们带着下面几个问题,来彻底理清Virtual DOM
:
- ✅ 什么是虚拟 DOM
- ✅ 为什么需要虚拟 DOM
- ✅ 在 React 中是如何落地实现的
- ✅ 虚拟 DOM 的优势与多平台渲染
🌱 1. 什么是虚拟 DOM?
虚拟 DOM(Virtual DOM)并不是一个新数据结构,而是一种编程思想。
它的核心理念是:把 UI 的状态保存为一种理想化(或者说抽象化、虚拟化)的结构,然后映射(render)成实际平台的渲染接口。
在 React 的官方 FAQ 里,有一句非常准确的定义:
The Virtual DOM is a concept where a virtual representation of the UI is kept in memory. (虚拟 DOM 是一种概念,指将 UI 的一种理想化表示保存在内存中。)
这就意味着:
-
虚拟 DOM 其实就是一种用来描述界面层次结构的中间层。
-
至于这个"虚拟结构"用什么来描述?可以是:
- JS 对象(React 的做法)
- JSON(某些轻量框架)
- 或者任何抽象数据结构
在 React 中,恰好选择用 JavaScript 对象来承载这种虚拟树。
所以:
虚拟 DOM 是一种思想,js对象只是 React 中具体的实现手段。
📝 2. 为什么需要虚拟 DOM?
⚡ JS 计算更快
浏览器中,DOM 是浏览器引擎管理的,并且为了渲染到屏幕,要经历:
- 解析 HTML → 构建 DOM Tree → Style → Layout → Paint → Composite
- 每次 DOM 的读写(尤其是触发重排/回流)都比较昂贵。
(1) 创建10000个js对象 vs 创建10000 DOM
js
// 计时开始
console.time();
const arr = [];
// 创建 10000 个 DOM 对象
for (let i = 0; i < 10000; i++) {
arr.push(document.createElement("div"));
}
// 计时结束 平均在 2.5ms 左右
console.timeEnd();
// 计时开始
console.time();
const list = [];
// 创建 10000 JS 对象
for(let i=0; i<10000; i++){
list.push({
tag : 'div'
});
}
// 计时结束 平均是在 0.4ms 左右
console.timeEnd();

因此:可以看出操作js对象的时间和操作DOM对象的时间是完全不一样的,js层面的计算速度要远高于DOM层面的计算速度。
(2)更新阶段的显著优势
那么此时有一个问题
最终还不是要把虚拟 DOM 转成真实 DOM,那不是多此一举吗?
其实:
-
在首次渲染时,虚拟 DOM 反而更慢一点(多了一层虚拟树)。
-
但在 更新阶段,虚拟 DOM 可以:
- 比较新旧虚拟 DOM 树(Diff)
- 只把必要的变化同步到真实 DOM
-
避免直接用
innerHTML
或大批量直接操作 DOM,导致整个节点都销毁再重建。
场景 | innerHTML | 虚拟 DOM |
---|---|---|
首次渲染 | 直接创建真实 DOM | 先转虚拟 DOM,再创建真实 DOM |
更新 UI | 直接销毁/重建所有节点 | Diff 算法后最小化 DOM 修改 |
性能消耗主要在 | DOM 解析与重绘 | JS 对象对比,避免多余 DOM 变更 |
(3)跨平台渲染抽象
虚拟 DOM 不仅仅是为了浏览器,因为它只是一个抽象的 UI
描述,所以可以:
-
在不同宿主环境下映射不同渲染引擎:
- 浏览器用
ReactDOM
渲染成真实 DOM - 原生应用用
React Native
渲染成原生组件 - Canvas / SVG 使用
React ART
- Node 里可以
ReactDOMServer
输出 HTML React Test Renderer
用来生成 JSON,做快照测试
- 浏览器用
通过这一层抽象,React 实现了真正的:
UI = f(state) 状态发生变化,React 重新计算虚拟 DOM,然后只把变化同步到真实宿主。
👀 3. React 中虚拟 DOM 的实现
在 React 中通过 JSX 来描述 UI,JSX 最终会被转为一个叫做 createElement
方法的调用,调用该方法后就会得到虚拟 DOM 对象。
jsx
<h1 id="title">Hello</h1>
JSX 只是一个语法糖,最终会被 Babel 编译成:
jsx
React.createElement('h1', { id: 'title' }, 'Hello')
在 React 源码里,对应如下:
jsx
/**
*
* @param {*} type 元素类型 div
* @param {*} config 属性对象 {id : "title"}
* @param {*} children 子元素 hello
* @returns
* <div id="title">hello</div>
*/
export function createElement(type, config, children) {
let propName;
const props = {};
let key = null;
let ref = null;
let self = null;
let source = null;
if (config != null) {
// ...
for (propName in config) {
if (
hasOwnProperty.call(config, propName) &&
!RESERVED_PROPS.hasOwnProperty(propName)
) {
props[propName] = config[propName];
}
}
}
// 经历了上面的 if 之后,所有的属性都放到了 props 对象上面
// props ==> {id : "title"}
// children 可以有多个参数,这些参数被转移到新分配的 props 对象上
// 如果是多个子元素,对应的是一个数组
const childrenLength = arguments.length - 2;
if (childrenLength === 1) {
props.children = children;
} else if (childrenLength > 1) {
const childArray = Array(childrenLength);
for (let i = 0; i < childrenLength; i++) {
childArray[i] = arguments[i + 2];
}
// ...
props.children = childArray;
}
// 添加默认的 props
if (type && type.defaultProps) {
const defaultProps = type.defaultProps;
for (propName in defaultProps) {
if (props[propName] === undefined) {
props[propName] = defaultProps[propName];
}
}
}
// ...
return ReactElement(
type,
key,
ref,
self,
source,
ReactCurrentOwner.current,
props
);
}
const ReactElement = function (type, key, ref, self, source, owner, props) {
// 该对象就是最终向外部返回的 vdom(也就是用来描述 DOM 层次结构的 JS 对象)
const element = {
// 让我们能够唯一地将其标识为 React 元素
$$typeof: REACT_ELEMENT_TYPE,
// 元素的内置属性
type: type,
key: key,
ref: ref,
props: props,
// 记录负责创建此元素的组件。
_owner: owner,
};
// ...
return element;
};
💡 最终ReactElement方法返回的element对象就是我们所说的虚拟DOM对象,所以在 React 中,虚拟 DOM ≈ React Element(JS 对象)。
🚀 4. 虚拟 DOM 的更新原理(Diff)
当状态发生变化时:
- React 会重新执行函数组件,生成新的虚拟 DOM 树。
- 用 Diff 算法(基于一些优化规则,比如同层比较、用 key 匹配)找到哪些节点需要更新。
- 只对比出来不同的部分进行 DOM 操作。
这样就避免了:
innerHTML
直接替换全部- 频繁操作 DOM,导致多次回流、重绘
🏆 5. 虚拟 DOM 的优势小结
✅ JS 端快
- 大部分 diff、计算在 JS 进行,避免频繁操作 DOM。
✅ 减少不必要的 DOM 变更
- 使用 diff 算法精准找到需要更新的节点。
✅ 跨平台渲染能力
- 同一套逻辑,支持浏览器、Native、Canvas、Server 端。
✅ 更好的可维护性
- UI = f(state),避免直接操作 DOM,减少副作用。
💬 6. 面试高分回答参考
📝 什么是虚拟 DOM?它的优点有哪些?
回答示例:
虚拟 DOM 本质上是一种编程思想,是用 JavaScript 对象在内存中对真实 DOM 结构进行抽象描述。在 React 中,使用
createElement
返回的就是虚拟 DOM,也叫 React Element。使用虚拟 DOM 有以下优点:
- 避免直接操作 DOM,主要在 JS 内存中 diff,比操作真实 DOM 更快。
- 通过 Diff 算法精确找出需要更新的节点,减少浏览器重排和重绘。
- 能作为跨平台渲染的中间层,不仅可以渲染到浏览器 DOM,还能渲染到原生组件(React Native)、Canvas(React ART)。
- 虚拟 DOM 发挥优势的时机主要体现在更新的时候,相比较 innerHTML 要将已有的 DOM 节点全部销毁,虚拟 DOM 能够做到针对 DOM 节点做最小程度的修改
因此虚拟 DOM 带来了更高的性能潜力与更强的可维护性。
🎉 7. 小结
✅ 虚拟 DOM 并不是 React 的专利,也并非一定要用 JS 对象,只是一种 UI 抽象化、函数化描述的思想。
✅ React 的虚拟 DOM 是这种思想的一个经典实现,它结合 Diff 算法,帮助我们在不同宿主环境下快速、精准地更新 UI。