在前端开发中,我们经常听到 虚拟 DOM (Virtual DOM) 这个概念。它是 React、Vue 等框架的核心机制之一,用来提升性能和简化开发。那么,虚拟 DOM 到底是什么?为什么要用它?又是如何工作的呢?本文将带你系统地理解虚拟 DOM。
1.什么是 DOM
在浏览器中,DOM ( Document Object Model ) 是 HTML 的结构化表示。它本质上是一棵树,节点对应页面上的元素。例如:
xml
<div id="app"> <h1>Hello</h1> <p>World</p> </div>
对应的 DOM 树大致是:
less
- div#app
- h1 (Hello)
- p (World)
DOM 提供了强大的 API,可以操作页面。但缺点是:真实 DOM 操作非常昂贵,频繁修改会导致页面性能下降。
2.什么是虚拟 DOM
虚拟 DOM 并不是浏览器原生提供的,而是框架用 JavaScript 对象 来模拟 DOM 结构的一层抽象。比如上面的 HTML 可以用虚拟 DOM 表示为:
css
const vdom =
{
type: 'div',
props: { id: 'app' },
children: [
{ type: 'h1', props: {}, children: ['Hello'] },
{ type: 'p', props: {}, children: ['World'] }
]
};
可以看到:
type
表示标签类型props
存储属性(id、class 等)children
表示子节点
这就是虚拟 DOM ------ 真实 DOM 的一个轻量级 JS 对象副本。
3.为什么需要虚拟 DOM?
-
提升性能
- 真实 DOM 操作慢:每次修改 DOM 都会引起回流(reflow)和重绘(repaint)。
- 虚拟 DOM 用 JS 对象计算变化,再一次性更新真实 DOM,减少开销。
-
跨平台
- 虚拟 DOM 不依赖浏览器 DOM,可以映射到不同平台,如 小程序、原生移动端、服务端渲染 等。
-
声明式 UI
- 开发者只需要描述"状态",框架通过虚拟 DOM 来决定"如何更新 DOM"。
-
中间层的作用
- 虚拟 DOM 作为 UI 与底层平台之间的中间层,屏蔽了底层差异。开发者只需要操作统一的虚拟 DOM 层,框架可以根据平台不同输出到浏览器 DOM、原生组件或其他渲染目标,从而实现"一份代码,多端运行"。
- 虚拟 DOM 的工作流程
虚拟 DOM 的核心机制是 Diff 算法 + Patch 过程:
-
创建虚拟 DOM
- 初始渲染时,框架会把模板/JSX 转换成虚拟 DOM。
-
Diff 算法对比
- 当状态改变时,生成新的虚拟 DOM。
- 对比新旧虚拟 DOM,找出差异。
-
Patch 更新真实 DOM
- 仅更新有变化的部分,而不是整个页面。
例如:
-
初始:
css<p>Hello</p>
-
更新后:
css<p>Hi</p>
虚拟 DOM 对比结果:只有 Hello → Hi
变了,于是只修改文本节点,而不是重新渲染整个 <p>
。
- React/Vue 中的虚拟 DOM
- React :使用
React.createElement
或 JSX 生成虚拟 DOM。React16 之后引入 Fiber 架构,优化调度性能。 - Vue 2 :通过
render
函数生成虚拟 DOM。 - Vue 3:借助 Proxy + 编译优化,减少不必要的虚拟 DOM 对比,性能更强。
-
虚拟 DOM 的误区
-
虚拟 DOM 并不总是比手动操作 DOM 快。
- 少量节点操作时,直接操作 DOM 可能更快。
- 虚拟 DOM 优势在于复杂应用中降低维护成本。
-
虚拟 DOM ≠ 必须
- Svelte 就是一个"无虚拟 DOM"框架,它通过编译时优化,直接生成高效的 DOM 更新代码。
- 总结
虚拟 DOM 的本质:
- 用 JavaScript 对象来描述 UI
- 通过 Diff 算法找到最小化更新路径
- 作为中间层屏蔽平台差异,实现多端渲染
- 高效、跨平台地更新真实 DOM
它的意义不只是性能优化,更是让前端开发以 声明式编程 的方式进行,让我们只关心"结果",而不用关心"过程"。
未来,随着编译型框架的发展(如 Svelte、SolidJS),虚拟 DOM 可能不再是唯一的选择,但在目前的生态下,它仍然是主流方案。