说说 Real DOM 和 Virtual DOM 的区别?优缺点?
一、是什么
1.1 Real DOM
DOM(Document Object Model)是浏览器对 HTML 文档的编程接口。浏览器解析 HTML 后生成一棵 DOM 树,每个节点都是一个 DOM 对象,包含了大量的属性和方法:
const div = document.createElement("div");
// 一个空 div 元素上有超过 200 个属性
console.log(Object.keys(div).length); // 200+
真实 DOM 节点是重量级对象。每次操作 DOM(创建、修改、删除节点)都可能触发浏览器的重排(Reflow)和重绘(Repaint),这是性能消耗的主要来源。
浏览器渲染一个页面的流程:
- 解析 HTML → 构建 DOM 树
- 解析 CSS → 构建 CSSOM 树
- 合并 DOM 和 CSSOM → 生成 Render 树
- 布局(Layout/Reflow)→ 计算每个节点的位置和大小
- 绘制(Paint)→ 将像素绘制到屏幕
每次 DOM 变更都可能重新执行第 3-5 步,代价昂贵。
1.2 Virtual DOM
虚拟 DOM 是真实 DOM 的轻量级 JavaScript 对象表示。它是 React 内部使用的一种编程概念,并非浏览器提供的 API。
// 真实 DOM 节点
const realDOM = document.createElement("div");
realDOM.className = "container";
realDOM.appendChild(document.createTextNode("Hello"));
// 对应的虚拟 DOM(简化)
const virtualDOM = {
type: "div",
props: {
className: "container",
children: "Hello",
},
key: null,
ref: null,
};
虚拟 DOM 对象只包含必要的描述信息,创建和比较的代价远低于真实 DOM 对象。
二、Virtual DOM 的工作原理
2.1 React.createElement
在 React 中,JSX 会被编译为 React.createElement(或 React 17+ 的 jsx 函数)调用,返回一个 ReactElement 对象,即虚拟 DOM 节点:
// JSX
const element = (
<div className="list">
<h1>标题</h1>
<ul>
<li>项目1</li>
<li>项目2</li>
</ul>
</div>
);
// 编译后(简化)
const element = {
$$typeof: Symbol.for("react.element"),
type: "div",
props: {
className: "list",
children: [
{ type: "h1", props: { children: "标题" } },
{
type: "ul",
props: {
children: [
{ type: "li", props: { children: "项目1" } },
{ type: "li", props: { children: "项目2" } },
],
},
},
],
},
};
$$typeof 是一个 Symbol 值,用于防止 XSS 攻击——JSON 无法表示 Symbol,因此恶意注入的 JSON 不会被 React 当作合法的 ReactElement 处理。
2.2 Diff 算法
当状态更新时,React 会生成新的虚拟 DOM 树,并与旧树进行比较(Reconciliation)。React 的 diff 算法基于三个假设:
- 不同类型的元素会产生不同的树:如果根元素类型不同,直接销毁旧树,创建新树
- 同一层级的子元素通过 key 来标识:通过 key 判断哪些元素是相同的,哪些需要移动
- 只做同层比较:不会跨层级比较节点
// 情况一:类型不同,整棵子树替换
// 旧树
<div><Counter /></div>
// 新树
<span><Counter /></span>
// → 销毁 div 及其子树,创建 span 及新的 Counter
// 情况二:类型相同,更新属性
// 旧树
<div className="old" title="旧" />
// 新树
<div className="new" title="旧" />
// → 只更新 className 属性
// 情况三:列表使用 key 优化
<ul>
<li key="a">A</li>
<li key="b">B</li>
</ul>
// →
<ul>
<li key="b">B</li>
<li key="a">A</li>
<li key="c">C</li>
</ul>
// → 通过 key 识别出 A、B 只需移动位置,C 是新增
2.3 批量更新
React 收集一段时间内的所有状态变更,合并后一次性更新 DOM:
function Counter() {
const [count, setCount] = useState(0);
function handleClick() {
setCount((c) => c + 1);
setCount((c) => c + 1);
setCount((c) => c + 1);
// React 只会触发一次重新渲染,最终 count 为 3
}
return <button onClick={handleClick}>{count}</button>;
}
这种批处理机制避免了中间状态的无效渲染,是虚拟 DOM 性能优势的重要组成部分。
三、Reconciliation 调和过程
React 的调和过程(Reconciliation)是虚拟 DOM 机制的核心,在 React 16+ 中由 Fiber 架构驱动:
状态变更 → 创建新 ReactElement 树
→ Fiber 调和(Render Phase)
→ 逐节点比较新旧 Fiber
→ 标记需要变更的节点(effectTag)
→ 提交更新(Commit Phase)
→ 执行 DOM 操作
→ 调用生命周期 / useEffect
Render Phase 是可中断的——React 可以暂停低优先级的更新,先处理用户交互等高优先级任务,这就是 React 18 并发渲染的基础。
function App() {
const [input, setInput] = useState("");
const [list, setList] = useState([]);
const [isPending, startTransition] = useTransition();
function handleChange(e) {
const value = e.target.value;
setInput(value); // 高优先级:立即更新输入框
startTransition(() => {
setList(generateHugeList(value)); // 低优先级:可中断
});
}
return (
<div>
<input value={input} onChange={handleChange} />
{isPending ? <p>加载中...</p> : <List items={list} />}
</div>
);
}
四、Real DOM vs Virtual DOM 对比
4.1 Virtual DOM 的优势
- 开发效率高:声明式编程让开发者专注于"是什么"而非"怎么做"
- 性能下限高:自动 diff + 批量更新保证了大多数场景下的良好性能
- 跨平台能力:虚拟 DOM 是平台无关的抽象层,可以渲染到浏览器、移动端、Canvas 等
- 可测试性:虚拟 DOM 是纯 JavaScript 对象,易于测试
4.2 Virtual DOM 的局限
- 内存开销:需要额外维护一棵 JavaScript 对象树
- 首次渲染不一定快:初始化时要同时创建虚拟 DOM 和真实 DOM
- 极端场景性能不如手动优化:手动精确操作 DOM 在特定场景下效率更高
4.3 什么时候 Real DOM 更快?
// 场景:已知只需修改某个元素的文本
// 直接操作 DOM 更高效
document.getElementById("counter").textContent = newCount;
// 而 React 需要:
// 1. 触发组件重新执行
// 2. 生成新的虚拟 DOM 树
// 3. diff 比较
// 4. 最终才更新这一个文本节点
在以下场景中,手动操作 Real DOM 可能更快:
- 已知精确的更新范围,且更新量极小
- 高频更新的动画(此时应使用 CSS 动画或 requestAnimationFrame)
- 大量 DOM 节点的一次性创建(如
innerHTML 一次性设置)
五、面试要点总结
虚拟 DOM 的核心价值不在于"它比真实 DOM 快"这个经常被误解的说法。更准确地说:
虚拟 DOM 提供了一种声明式的编程模型,让开发者不需要手动操作 DOM,同时通过 diff 算法和批量更新机制,保证了在大多数场景下足够好的性能。它是开发体验与性能之间的最优平衡。
尤雨溪(Vue 作者)的经典总结也适用于 React:
- 没有任何框架可以比手动优化的原生 DOM 操作更快
- 框架的意义在于,不需要手动优化的情况下,依然能提供过得去的性能
- 虚拟 DOM 真正的价值是声明式 UI + 跨平台渲染的能力
在 React 19 中,React Compiler 的出现进一步证明了虚拟 DOM 架构的优势:编译器可以在构建时自动分析组件依赖,插入记忆化代码,减少不必要的虚拟 DOM 创建和 diff,让这套架构变得更高效。