DOM 树、CSSOM 树和渲染树有什么关系?
简化版
DOM 树由 HTML 解析生成,表示页面结构;CSSOM 树由 CSS 解析生成,表示样式规则;浏览器把 DOM 和 CSSOM 结合生成渲染树,渲染树只包含需要显示的节点,然后进行布局、绘制和合成。
详细版
DOM 描述“页面有什么元素”,CSSOM 描述“元素应该长什么样”。浏览器需要两者都准备好,才能准确渲染页面。
渲染树不等于 DOM 树。例如 display: none 的节点不会进入渲染树,但 visibility: hidden 的节点仍占据布局空间,通常仍参与布局。
CSS 会阻塞渲染,因为缺少样式时浏览器无法确定最终视觉效果。JS 也可能阻塞解析,因为 JS 可以修改 DOM 和样式。
完整版教学
一、DOM 树
浏览器解析 HTML 时,会把标签、文本、属性转换成 DOM 节点,并组织成树结构。
DOM 是 JS 可以操作的文档对象模型。前端代码通过 DOM API 修改页面结构,本质上就是修改这棵树或树上的节点属性。
二、CSSOM 树
浏览器解析 CSS 文件、style 标签、内联样式,生成 CSSOM。CSSOM 记录选择器、继承、层叠、优先级和最终样式计算所需信息。
CSSOM 构建完成前,浏览器难以正确绘制页面,所以 CSS 是关键渲染路径中的重要资源。
三、渲染树
渲染树由可见 DOM 节点和它们的计算样式组成。它不包含不显示的节点,例如 head、script、display: none 的元素。
渲染树生成后,浏览器进行布局,计算每个节点的位置和尺寸;再绘制像素;最后合成显示。
四、面试追问与工程落地
面试官可能问:“display:none 和 visibility:hidden 对渲染树有什么影响?”
display: none 会让元素不参与渲染树,不占布局空间。visibility: hidden 元素不可见,但仍占据布局空间,因此仍会影响布局。
工程中,如果要频繁显示隐藏复杂节点,可以根据场景选择 display、visibility、opacity 或条件渲染。不同选择对布局、绘制和交互命中都有影响。
五、样式计算与树并非一一对应
浏览器不是简单把一棵 DOM 和一棵 CSSOM 按节点相加。它要为元素匹配选择器、处理层叠、继承和默认样式,得到 computed style,再为需要布局和绘制的内容建立内部布局/渲染结构;不同引擎的内部树名称和拆分方式也可能不同。
| DOM/样式情况 | DOM 中存在 | 占布局空间 | 是否绘制可见内容 |
|---|---|---|---|
display: none | 是 | 否 | 否 |
visibility: hidden | 是 | 是 | 否 |
opacity: 0 | 是 | 是 | 透明但仍参与命中规则 |
::before 生成内容 | 无独立 DOM 节点 | 可占 | 可绘制 |
因此“渲染树只包含 DOM 可见节点”只是入门近似。伪元素可能没有对应 DOM 节点却产生渲染盒,opacity:0 看不见但仍参与布局,visibility:hidden 的后代还有继承与覆盖边界;面试应围绕是否生成盒、布局、绘制和命中分别判断。
六、增量更新与关键路径成本
DOM 或 CSSOM 改变后,浏览器通常只让受影响范围失效并重新计算,而不是每次重建全页面。改变祖先字体可能影响大量后代,给独立叶节点改颜色范围更小;具体代价由节点数、选择器匹配、布局依赖和图层决定,不能只按“改 CSS”判断。
HTML bytes → tokens → DOM
CSS bytes → rules → CSSOM
DOM + CSSOM → computed style → layout boxes → paint records → layers
假设页面有 10,000 个节点,给根节点切换影响继承的主题类,可能让大量节点重新计算样式;只切换一个隔离卡片的类,影响范围小得多。CSS 选择器微优化通常不是首要矛盾,先减少无效 DOM、控制样式作用域并用 Performance 面板测量。
外部 CSS 阻塞首次渲染,是因为后续规则可能覆盖前面结果;可以内联首屏关键 CSS、拆分非关键媒体样式并压缩,但过度内联会失去共享缓存。优化是在首屏阻塞字节与长期缓存复用之间取舍。
记忆钩子:DOM 回答“文档是什么”,CSSOM 提供“规则是什么”,浏览器计算后的布局盒与绘制记录才回答“像素怎么来”。
可见性、交互与无障碍不是同一棵树
浏览器还维护可访问性树、事件命中和合成图层等结构。一个元素视觉透明不代表屏幕阅读器一定忽略,也不代表指针事件一定穿透;需要隐藏时要同时明确视觉、布局、交互和无障碍语义。
例如弹窗关闭只设 opacity:0,它可能仍占布局、接收点击并被辅助技术感知。根据需求组合 display/hidden、visibility、pointer-events、aria-hidden 或 inert,而不是用一个样式属性推导所有行为。
调试判断顺序
遇到“元素存在却看不见”时,依次检查 DOM 是否存在、computed style、是否生成布局盒、尺寸位置、遮挡/裁剪、透明度和图层。这个顺序比只搜索 display:none 更完整,也能区分样式匹配、布局和绘制问题。
DevTools 的 Elements、Computed、Layout 与 Layers/Rendering 工具分别提供不同证据;不要从 DOM 面板中有节点就推断它一定产生像素。
七、常见误区与追问
- 误区:渲染树与 DOM 树节点一一对应。
display:none节点不生成布局盒,伪元素却可能无 DOM 节点而生成可绘制内容。 - 误区:
opacity:0和display:none效果完全相同。 前者仍参与布局,交互命中也需额外处理;后者不生成布局盒。 - 误区:每次改一个样式都会完整重建 DOM、CSSOM 和渲染树。 浏览器会做失效与增量更新,影响范围取决于依赖关系。
- 追问:CSS 为什么阻塞首次渲染? 后续规则可能改变任何已匹配元素的最终样式,浏览器需要足够 CSSOM 信息才能稳定绘制。
- 追问:伪元素为什么能显示却查不到 DOM 节点? 它由 CSS 生成渲染内容,不是 HTML 解析产生的独立 DOM 元素。
- 追问:
visibility:hidden是否触发布局? 元素仍保留原布局空间;切换 visibility 通常不需要改变几何布局,但可能需要绘制更新。 - 追问:如何定位样式计算慢? 用 Performance 面板查看 Recalculate Style,结合受影响节点数和失效原因,而不是先猜选择器。
八、加强记忆
DOM、CSSOM 和渲染结构按“内容、规则、结果”记:HTML 形成文档节点,CSS 形成层叠规则,浏览器匹配后生成计算样式、布局盒和绘制记录。节点是否在 DOM、是否占空间、是否绘制、是否可命中是四个不同问题;优化时关注受影响范围与关键 CSS,而不是把所有变化都说成整棵树重建。