大 DOM 大小对交互性的影响比您想象的要大。本指南介绍了原因以及您可以采取的行动。
这种解决办法无法解决:当您构建某个网页时,该网页将拥有一个文档对象模型 (DOM)。DOM 代表网页 HTML 的结构,可以通过 DOM 访问网页的结构和内容。
不过,问题在于 DOM 的大小会影响浏览器快速高效地渲染网页的能力。一般来说,DOM 越大,最初渲染该网页以及在网页生命周期中稍后更新其渲染的开销就越大。
当修改或更新 DOM 的互动触发开销大的布局工作,影响页面快速响应的能力时,这会在具有非常大 DOM 的页面中出现问题。成本高昂的布局工作可能会影响页面的 Interaction to Next Paint (INP);如果您希望页面能够快速响应用户互动,请务必确保 DOM 大小仅根据需要设置。
页面的 DOM 何时过大?
根据 Lighthouse 的介绍,当页面超过 1,400 个节点时,其 DOM 大小会过大。当页面的 DOM 超过 800 个节点时,Lighthouse 将开始发出警告。以下面的 HTML 为例:
<ul>
<li>List item one.</li>
<li>List item two.</li>
<li>List item three.</li>
</ul>
以上代码中有四个 DOM 元素:<ul>
元素及其三个 <li>
子元素。您网页的节点数量几乎肯定会远远多于此数量,因此,在将页面的 DOM 降至尽可能小后,了解您可以如何控制 DOM 大小以及优化渲染工作的其他策略非常重要。
大型 DOM 如何影响页面性能?
大型 DOM 会以以下几种方式影响页面性能:
- 在网页的初始呈现期间。将 CSS 应用于网页时,系统会创建一个与 DOM 类似的结构,称为 CSS 对象模型 (CSSOM)。随着 CSS 选择器的针对性越来越强,CSSOM 变得更加复杂,需要更多的时间来运行必要的布局、样式设置、合成和绘制工作,从而将网页绘制到屏幕上。对于在网页加载早期发生的互动,这项额外工作会增加互动延迟时间。
- 当交互通过插入或删除元素,或者通过修改 DOM 内容和样式来修改 DOM 时,渲染该更新所需的工作可能成本非常高昂的布局、样式、合成和绘制工作。与网页的初始渲染一样,当 HTML 元素因互动而插入 DOM 时,提高 CSS 选择器的特异性可能会增加渲染工作。
- 当 JavaScript 查询 DOM 时,对 DOM 元素的引用存储在内存中。例如,如果您调用
document.querySelectorAll
来选择页面上的所有<div>
元素,并且结果返回大量 DOM 元素,那么内存开销可能会相当可观。
所有这些都会影响互动,但上述列表中的第二项尤为重要。如果互动导致 DOM 发生变化,就会触发大量工作,进而导致网页上的 INP 不佳。
如何测量 DOM 大小?
您可以通过几种方式测量 DOM 大小。第一种方法使用 Lighthouse。进行审核时,有关当前页面的 DOM 的统计信息将位于“避免 DOM 规模过大”查看“诊断”下的标题。在此部分中,您可以看到 DOM 元素的总数、包含最多子元素的 DOM 元素以及最深的 DOM 元素。
一种比较简单的方法是使用任何主流浏览器的开发者工具中的 JavaScript 控制台。要获取 DOM 中 HTML 元素的总数,您可以在页面加载后在控制台中使用以下代码:
document.querySelectorAll('*').length;
如果要实时查看 DOM 大小更新,还可以使用性能监控工具。利用此工具,您可以将布局和样式设置操作(以及其他性能方面)与当前 DOM 大小相关联。
如果 DOM 的大小接近 Lighthouse DOM 的警告阈值或者完全失败,那么下一步就是了解如何减小 DOM 的大小,以提高网页对用户互动的响应能力,从而提高网站的 INP。
如何衡量受互动影响的 DOM 元素数量?
如果您在实验中分析了一项缓慢的交互,并且怀疑可能与页面 DOM 的大小有关,那么可以通过在性能分析器中选择标有“重新计算样式”的任何 activity 来确定受影响的 DOM 元素数量并观察底部面板中的情境数据。
在上面的屏幕截图中,您可以看到作品的样式重新计算(选中后)显示了受影响元素的数量。以上屏幕截图显示了 DOM 大小对包含很多 DOM 元素的页面上的渲染工作的极端情况,此诊断信息在任何情况下都很有用,可以确定 DOM 大小是否是影响下一帧绘制以响应交互所需时间的限制因素。
如何减小 DOM 大小?
除了检查网站的 HTML 是否存在不必要的标记外,减小 DOM 大小的主要方法是减少 DOM 深度。如果您在浏览器的开发者工具的元素标签页中看到了类似如下内容的标记,则表明您的 DOM 可能过深:
<div>
<div>
<div>
<div>
<!-- Contents -->
</div>
</div>
</div>
</div>
当您看到这样的模式时,可以通过展平 DOM 结构来简化这些模式。这样做可以减少 DOM 元素的数量,并有可能帮助您简化网页样式。
DOM 深度也可能是您使用的框架的特征。特别是,基于组件的框架(例如那些依赖于 JSX 的框架)要求您将多个组件嵌套在一个父级容器中。
不过,许多框架都支持您使用 fragment 来避免嵌套组件。将 fragment 作为功能提供且基于组件的框架包括(但不限于)以下内容:
通过在所选框架中使用 fragment,您可以降低 DOM 深度。如果您担心展平 DOM 结构对样式设置的影响,那么使用更现代(更快)的布局模式(例如 flexbox 或 grid)可能会让您受益。
可以考虑的其他策略
即使您费力地展平了 DOM 树并移除了不必要的 HTML 元素,以尽可能减小 DOM 的体积,该 DOM 依然会非常庞大,并且随着其不断变化来响应用户互动,这些渲染工作仍可能引发大量的渲染工作。如果您属于这种情况,可以考虑采取一些其他策略来限制呈现工作。
考虑附加方法
您可能处于网页首次呈现时用户看不到的大部分内容的位置。这可以通过在启动时省略 DOM 的这些部分来延迟加载 HTML,但在用户与需要页面最初隐藏方面的页面部分互动时添加这些部分。
这种方法在初始加载期间乃至加载后都很有用。对于初始网页加载,您预先承担的渲染工作量较少,这意味着初始 HTML 有效负载将会更轻量,并且渲染速度更快。这样,在这一关键阶段,互动就有更多机会运行,从而减少针对主线程注意力的竞争。
如果网页中有许多部分最初在加载时处于隐藏状态,这也会加快其他触发重新呈现工作的互动速度。不过,随着其他互动为 DOM 添加更多内容,渲染工作量将随着 DOM 在整个页面生命周期内不断扩大而增多。
随着时间的推移,向 DOM 添加内容并非易事,这也是各有利弊。如果您采用这种方式,则可能会发出网络请求,以获取数据来填充您打算添加到网页的 HTML,以响应用户互动。虽然传输中的网络请求不会计入 INP,但可能会增加感知延迟时间。如果可能,请显示一个表示正在提取数据的加载旋转图标或其他指示器,以便用户了解正在发生的情况。
限制 CSS 选择器的复杂性
当浏览器解析 CSS 中的选择器时,它必须遍历 DOM 树来了解这些选择器如何(以及是否)应用于当前布局。这些选择器越复杂,浏览器需要进行的工作就越多,这样浏览器才能执行网页的初始渲染,而且在网页由于互动而发生变化时,样式重新计算和布局工作也会增加。
使用 content-visibility
属性
CSS 提供了 content-visibility
属性,这实际上是一种延迟渲染屏幕外 DOM 元素的方式。当这些元素接近视口时,它们便会按需呈现。content-visibility
的优势不仅可在网页首次渲染时省去大量的渲染工作,而且当页面 DOM 因用户互动而发生变化时,还能跳过屏幕外元素的渲染工作。
总结
将 DOM 大小缩减为绝对必要的大小是优化网站 INP 的好方法。这样做可以减少 DOM 更新后浏览器执行布局和渲染工作所需的时间。即使您无法有效减小 DOM 大小,您也可以使用一些技术(例如 CSS 包含和 content-visibility
CSS 属性)将渲染工作隔离到 DOM 子树中。
无论您如何着手,营造一个最大程度减少渲染工作的环境,并减少网页为响应互动而执行的渲染工作量,都会使您的网站在用户与用户互动时感觉更灵敏。这意味着您网站的 INP 将会降低,从而带来更好的用户体验。
主打图片来自 Louis Reed 的 Unsplash 网站。