DOM 树是浏览器理解并呈现网页的"骨架" 。无论你用的是 Vue、React 还是原生 JS,所有前端框架最终都在操作这棵树。
结合你的学习进度(Vue3 虚拟 DOM、构建工具),我带你彻底搞懂这棵"树"的物理结构 、生成过程 ,以及Vue 为什么要搞一个"虚拟 DOM"来模拟它。
1. 什么是 DOM 树?(纸上建筑模型)
当浏览器(如 Chrome)加载你的 index.html 时,它做的第一件事不是画图,而是**"读代码,搭积木"**。
定义 :DOM(文档对象模型,Document Object Model)树是一个内存中的多叉树结构 ,它用节点(Node) 的形式完整地表达了 HTML 文档的层级关系。
类比 :想象一栋房子的建筑结构图。
- 根节点(
document):就是这块地皮。 <html>:房屋的主体框架。<head>和<body>:房子的水电管道和居住空间。<div>、<p>、<button>:房间里的墙壁、家具和开关。
代码映射:
<!DOCTYPE html>
<html>
<head>
<title>我的页面</title>
</head>
<body>
<div id="app">
<h1>标题</h1>
<p>段落</p>
</div>
</body>
</html>
对应的 DOM 树逻辑结构:
document
└── html
├── head
│ └── title
│ └── "我的页面"
└── body
└── div#app
├── h1
│ └── "标题"
└── p
└── "段落"
2. 这棵树由哪些"积木"(节点)组成?
DOM 树由 不同类型的节点(Node) 构成,你在 Vue 模板中写的标签,最终都会变成以下 3 种主要节点:
| 节点类型 | 例子 | 描述 |
|---|---|---|
| 元素节点(Element) | <div>, <p>, <button> |
构成网页结构的骨架,可以嵌套子节点。 |
| 文本节点(Text) | "标题", "段落" |
叶子节点,没有子级,存放真正的文字内容。 |
| 属性节点(Attribute) | id="app", class="box" |
挂在元素节点上,不属于树的主干(通常不单独算作树的层级)。 |
3. 浏览器是如何"造"出这棵树的?(关键流程)
当你输入网址并回车,浏览器的渲染引擎(如 Chrome 的 Blink)会经历:
- 词法分析(Tokens):将 HTML 字符串拆解成开始标签、结束标签、属性、文本。
- 构建节点(Nodes):根据标签创建对应的 JS 对象。
- 建立父子关系 :遇到
<div>就作为当前父节点的最后一个儿子插入,形成 深度优先(DFS) 的树结构。 - 触发关键渲染路径 :DOM 树构建完成后,会与 CSSOM 树(样式树) 合并成 Render Tree(渲染树),最终绘制成屏幕上的像素。
4. 致命的痛点:为什么直接操作 DOM 树很"昂贵"?
在原生 JS 中,你写 document.getElementById('app').innerHTML = 'xxx',就是直接修改这棵树。
痛点:
- 回流(Reflow) :当你改了
width或display,浏览器不仅要改这棵树,还要重新计算所有子元素的几何位置(就像你抽走了积木底层的方块,整座塔都要垮塌重建)。 - 重绘(Repaint) :即使只改了
color这种不改变布局的属性,浏览器也得重新把像素画一遍。
结论:每秒操作 60 次 DOM 树,很容易造成页面卡顿(掉帧)。
5. Vue 3 的救命稻草:虚拟 DOM(Virtual DOM)与 Diff 算法
既然操作真树这么贵,Vue 就在 JS 内存里造了一棵**"假树(虚拟 DOM)"**。
运作流程:
- 造梦 :你写了
<template>,Vue 编译成render函数,生成一个轻量的 JS 对象(虚拟 DOM 树)。 - 挂载:Vue 拿着这棵虚拟树,一次性创建真实的 DOM 树。
- 变化 :数据变了(比如
message.value变了),Vue 生成新的虚拟树。 - 对比(Diff) :Vue 拿出 "新树" 和 "老树" 对比,找出具体是哪个节点、哪个属性变了(这个算法叫 Patch)。
- 打补丁 :Vue 只去修改那一个真实的 DOM 节点,其他几百个没变的节点原封不动。
这就是你之前学的
v-for必须加:key的底层原理 ::key是给虚拟 DOM 树上的节点打上的"身份证号",让 Diff 算法能精确判断是"移动了位置"还是"完全新建",从而极大提升 DOM 树的复用效率!
6. 结合你的代码看,DOM 树在哪里?
在你的 Vue 父组件中:
<template>
<div class="parent-component"> <!-- 1. 真实 DOM 树的起点 -->
<h3>👨👦 父组件</h3> <!-- 2. 文本节点 -->
<ChildComponent /> <!-- 3. 这是 Vue 的"组件节点",在 DOM 树上会被替换为组件内部的真实元素 -->
</div>
</template>
- 最终在浏览器控制台(F12 -> Elements)中看到的,全是真实的 DOM 树节点。
- 而你的
ChildComponent在编译阶段,会被 Vue 打平或插入,最终成为真实 DOM 树上的一个<div>子树。
7. Windows 11 开发者工具中的上帝视角
想亲眼看这棵树长什么样?在 Chrome/Edge 中:
- 按 F12 打开开发者工具。
- 点击 Elements(元素) 标签。
- 你看到的那个可折叠、缩进的层级结构 ,就是当前页面在内存中的 真实 DOM 树!
- 如果你开了 Vue Devtools,你看到的是一棵虚拟组件树,它更清晰,但不完全等于 DOM 树。
总结一句话记忆
DOM 树是浏览器内存中一颗由 HTML 标签构成的"家族谱系树",操作它非常消耗性能;而 Vue 在 JS 里造了一颗"模拟树(虚拟 DOM)",通过 Diff 算法精准只修改真实树上的必要部分,这就是 Vue 性能优化的核心密码。
现在打开你正在运行的项目,按 F12 看 Elements,再打开 Vue Devtools 看组件树,对比一下两者的映射关系------以后碰到性能问题,你脑海里就会浮现这两棵树的对比图!😄