DOM 大小如何影响互动性,以及您可以如何应对

大型 DOM 大小对互动性的影响比您想象的要大。本指南解释了原因以及您可以采取的措施。

这是不可避免的:当您构建网页时,该页面将具有文档对象模型 (DOM)。DOM 表示您页面 HTML 的结构,并允许 JavaScript 和 CSS 访问页面的结构和内容。

然而,问题是 DOM 的大小会影响浏览器快速有效地渲染页面的能力。一般来说,DOM 越大,初始渲染该页面以及稍后在页面生命周期中更新其渲染的成本就越高。

当具有非常大的 DOM 的页面中,修改或更新 DOM 的交互触发昂贵的布局工作,从而影响页面快速响应能力时,这就会变得有问题。昂贵的布局工作会影响页面的与下次绘制的交互 (INP);如果您希望页面快速响应用户交互,务必确保您的 DOM 大小仅在必要的范围内。

页面的 DOM 何时过大

根据 Lighthouse 的说法,当页面的 DOM 大小超过 1,400 个节点时,则认为其过大。当页面的 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 会通过以下几种方式影响页面性能

  1. 在页面的初始渲染期间。当 CSS 应用于页面时,会创建一个类似于 DOM 的结构,称为 CSS 对象模型 (CSSOM)。随着 CSS 选择器的特异性增加,CSSOM 变得更加复杂,并且需要更多时间来运行必要的布局、样式、合成和绘制工作,以将网页绘制到屏幕上。这种额外的工作会增加页面加载早期发生的交互的交互延迟。
  2. 当交互通过元素插入或删除,或通过修改 DOM 内容和样式来修改 DOM 时,渲染该更新所需的工作可能会导致非常昂贵的布局、样式、合成和绘制工作。与页面初始渲染的情况一样,当 HTML 元素作为交互的结果插入到 DOM 中时,CSS 选择器特异性的增加会增加渲染工作。
  3. 当 JavaScript 查询 DOM 时,对 DOM 元素的引用存储在内存中。例如,如果您调用 document.querySelectorAll 来选择页面上的所有 <div> 元素,如果结果返回大量 DOM 元素,则内存成本可能会相当高昂。
A screenshot of a long task caused by excessive rendering work in the performance panel of Chrome DevTools. The long task's call stack shows significant time spent recalculating page styles, as well as pre-paint.
Chrome DevTools 性能分析器中显示的长任务。显示的长任务是由通过 JavaScript 将 DOM 元素插入到大型 DOM 中引起的。

所有这些都可能影响互动性,但上面列表中的第二项尤为重要。如果交互导致 DOM 发生更改,则可能会启动大量工作,从而导致页面上的 INP 较差。

如何衡量 DOM 大小?

您可以通过多种方式衡量 DOM 大小。第一种方法是使用 Lighthouse。当您运行审核时,当前页面 DOM 的统计信息将位于“诊断”标题下的“避免过大的 DOM 大小”审核中。在此部分中,您可以看到 DOM 元素的总数、包含最多子元素的 DOM 元素以及最深的 DOM 元素。

更简单的方法是使用任何主要浏览器中开发者工具中的 JavaScript 控制台。要获取 DOM 中 HTML 元素的总数,您可以在页面加载后在控制台中使用以下代码

document.querySelectorAll('*').length;

如果您想实时查看 DOM 大小更新,您还可以使用 性能监视器工具。使用此工具,您可以将布局和样式操作(以及其他性能方面)与当前 DOM 大小相关联。

A screenshot of the performance monitor in Chrome DevTools. At left, there are various aspects of page performance that can be continuously monitored during the life of the page. In the screenshot, the number of DOM nodes, layouts per second, and style recalculations per section are actively being monitored.
Chrome DevTools 中的性能监视器。在此视图中,页面的当前 DOM 节点数与每秒执行的布局操作和样式重新计算一起绘制。

如果 DOM 的大小接近 Lighthouse DOM 大小的警告阈值(或完全失败),则下一步是弄清楚如何减小 DOM 的大小,以提高页面响应用户交互的能力,从而提高网站的 INP。

如何衡量受交互影响的 DOM 元素数量?

如果您正在实验室中分析一个缓慢的交互,并且您怀疑这可能与页面 DOM 的大小有关,您可以弄清楚有多少 DOM 元素受到影响,方法是选择分析器中标记为“重新计算样式”的任何活动片段,并观察底部面板中的上下文数据。

A screenshot of selected style recalculation activity in the performance panel of Chrome DevTools. At top, the interactions track shows a click interaction, and the majority of the work is spent doing style recalculation and pre-paint work. At the bottom, a panel shows more detail for the selected activity, which reports that 2,547 DOM elements were affected.
观察 DOM 中受样式重新计算工作影响的元素数量。请注意,交互轨道中交互的阴影部分表示交互持续时间超过 200 毫秒的部分,这是 INP 的指定“良好”阈值

在上面的屏幕截图中,观察到所选工作的样式重新计算显示了受影响元素的数量。虽然上面的屏幕截图显示了 DOM 大小对具有许多 DOM 元素的页面上的渲染工作的影响的极端情况,但在任何情况下,此诊断信息都很有用,可以确定 DOM 的大小是否是限制响应交互的下一帧绘制所需时间的因素。

如何减小 DOM 大小?

除了审核您网站 HTML 中不必要的标记外,减小 DOM 大小的主要方法是减小 DOM 深度。如果您在浏览器的开发者工具的 Elements 选项卡中看到如下所示的标记,则表明您的 DOM 可能不必要地过深

<div>
  <div>
    <div>
      <div>
        <!-- Contents -->
      </div>
    </div>
  </div>
</div>

当您看到这样的模式时,您可能可以通过展平 DOM 结构来简化它们。这样做将减少 DOM 元素的数量,并可能让您有机会简化页面样式。

DOM 深度也可能是您使用的框架的症状。特别是,基于组件的框架(例如那些依赖于 JSX 的框架)要求您将多个组件嵌套在父容器中。

但是,许多框架允许您通过使用所谓的片段来避免嵌套组件。提供片段作为功能的基于组件的框架包括(但不限于)以下框架

通过在您选择的框架中使用片段,您可以减小 DOM 深度。如果您担心展平 DOM 结构对样式的影响,您可能会受益于使用更现代(且更快)的布局模式,例如 flexboxgrid

其他需要考虑的策略

即使您费尽心思展平 DOM 树并删除不必要的 HTML 元素以尽可能缩小 DOM,它仍然可能非常大,并且在响应用户交互而发生更改时启动大量渲染工作。如果您发现自己处于这种情况,您可以考虑一些其他策略来限制渲染工作。

考虑一种添加方法

您可能处于这样一种情况,即当页面首次渲染时,页面的大部分内容最初对用户不可见。这可能是延迟加载 HTML 的机会,方法是在启动时省略 DOM 的这些部分,但在用户与页面中需要页面最初隐藏方面的部分进行交互时添加它们。

这种方法在初始加载期间甚至之后都很有用。对于初始页面加载,您预先承担的渲染工作较少,这意味着您的初始 HTML 有效负载将更轻,并且渲染速度会更快。这将为关键时期内的交互提供更多机会,使其在主线程的注意力竞争较少的情况下运行。

如果您有许多页面部分在加载时最初是隐藏的,这也可能会加快触发重新渲染工作的其他交互。但是,随着其他交互向 DOM 添加更多内容,渲染工作将随着 DOM 在整个页面生命周期中增长而增加。

随着时间的推移向 DOM 添加内容可能很棘手,并且它有其自身的权衡。如果您要走这条路线,您可能正在发出网络请求以获取数据,从而填充您打算响应用户交互而添加到页面中的 HTML。虽然正在进行的网络请求不计入 INP,但它可能会增加感知延迟。如果可能,请显示加载微调器或其他指示符,表明正在获取数据,以便用户了解正在发生某些事情。

限制 CSS 选择器复杂性

当浏览器解析 CSS 中的选择器时,它必须遍历 DOM 树才能了解这些选择器如何以及是否应用于当前布局。这些选择器越复杂,浏览器在执行页面的初始渲染以及在页面因交互而发生更改时执行增加的样式重新计算和布局工作时必须做的工作就越多。

使用 content-visibility 属性

CSS 提供了 content-visibility 属性,它实际上是一种延迟渲染屏幕外 DOM 元素的方法。当元素接近视口时,它们会按需渲染。content-visibility 的好处不仅在于减少了初始页面渲染时的大量渲染工作,而且还跳过了当页面 DOM 因用户交互而更改时对屏幕外元素的渲染工作。

结论

将 DOM 大小缩小到仅严格必要的程度是优化网站 INP 的好方法。这样做,您可以减少浏览器在 DOM 更新时执行布局和渲染工作所需的时间。即使您无法有意义地减小 DOM 大小,您也可以使用一些技术将渲染工作隔离到 DOM 子树,例如 CSS 包含和 content-visibility CSS 属性。

无论您如何操作,创建一种最小化渲染工作的环境,以及减少页面响应交互时执行的渲染工作量,最终结果都将是您的网站在用户与之交互时感觉更灵敏。这意味着您的网站将具有更低的 INP,这会转化为更好的用户体验。