你的 DOM 节点深度有多深?

原文信息: 查看原文查看原文

How Deep is Your DOM?

- Maxi Ferreira

如果你使用Lighthouse来衡量你网站的性能,你可能之前已经看到过避免过大的DOM尺寸的警告。它看起来像这样:

Lighthouse报告截图,显示有关过大DOM尺寸的警告

Lighthouse警告我们关于大DOM尺寸的问题,因为它们会增加内存使用量,并可能产生昂贵的样式计算。结合你网站上发生的所有其他事情,这可能会对用户体验产生重大影响,特别是对于低端设备的用户。

前几天,当我阅读我的网站的性能报告时,这个警告引起了我的注意。但不是我再次审视的DOM元素总数;而是下面报告的度量标准—最大DOM深度

当我在写上周的通讯时,花了很多时间思考树(数据结构,不是森林中生长的那些),树的深度与时间复杂性之间的关系仍然记忆犹新。所以在Lighthouse报告中看到这个度量标准立即引发了一个问题:

🤔 DOM深度如何影响渲染性能?

当我们使用像DOM这样的树形数据结构时,它的深度与在它们上执行查找等操作的速度有很大关系。看看这两个DOM树:

一个浅层的DOM树和一个深层的DOM树,两者都有相同数量的元素

一个浅层的DOM树和一个深层的DOM树,两者都有相同数量的元素。

两棵树都有相同数量的元素,但一棵的深度(或高度)为2,而另一棵为6。这个区别很重要,因为树越深,访问它的一个元素可能需要更多的操作。

例如,想象我们要从树的根开始访问<img>元素。在浅层树中,我们只需要两个操作就可以做到—找到<body>的子元素数组,并访问索引为4的子元素:

body.children[4];

另一方面,在深层树中,我们需要六次跳跃才能到达同一个元素:

body.children[0].children[0].children[0].children[0].children[0];

当我们处理某些类型的树时,树的高度特别重要,例如二叉搜索树(BST)。这就是为什么有这么多不同的数据结构实现自平衡BST的原因—这样我们可以在树增长时尽可能保持树的高度最小,并保持查找等操作尽可能快。

那么,回到DOM。我们知道理论上树越深,速度就越慢。但实际上这对性能有什么影响呢?

让我们做一个小实验来找出答案。

一个小实验

这里报告的所有指标都来自于在M1 Max MacBook上使用Google Chrome 125(无痕模式)运行性能配置文件的结果(4x CPU减速)。你可以在GitHub上查看测试页面的源代码。

为了测试这一点,我整理了几个只包含三行文本和100个空div的HTML页面。两个页面之间唯一的区别是,一个页面的所有div直接在文档的body中,而在另一个页面上,div是嵌套的。

所以,具有浅层元素树的那个看起来像这样:

<html>
  <body>
    <div></div>
    <div></div>
    <div></div>
    <div></div>
    <!-- 95个div之后... -->
    <div>这是100个div中的最后一个。</div>
  </body>
</html>

而具有深层树的那个看起来像这样:

<html>
  <body>
    <div>
      <div>
        <div>
          <div>
            <!-- 95个div之后... -->
            <div>这是100个div中的最后一个。</div>
          </div>
        </div>
      </div>
    </div>
  </body>
</html>

在第一页上运行性能配置文件,我得到了一个总的页面加载时间(解析+渲染+绘制)为51毫秒。而在第二页上,页面加载时间是惊人的……53毫秒

所以,这有点令人失望。

当然,这不是一个非常有意义的示例,因为所有的div都是空的,浏览器几乎只是在屏幕上渲染了几行文本,但我仍然期望看到更大的差异。

幸运的是,并且多亏了编程的魔力,测试更多的div只需要敲几下键盘。所以我测试了200个,然后是300个,然后是400个。在500个div时,性能有了明显的差异。

性能报告加载页面,页面中有500个嵌套的div,用了100多毫秒

性能报告加载页面,页面中有500个嵌套的div,用了100多毫秒。

浅层示例在56毫秒内渲染了所有500个div—基本上不受它需要处理的HTML量增加5倍的影响。但现在嵌套树需要102毫秒—几乎是两倍。

所以我继续测试,测试了多达5,000个div。这是结果:

DOM元素

值得注意的是,具有浅层树的页面和具有嵌套树的页面在大小上是相同的。它们都包含相同数量的元素,并且在浏览器中呈现时看起来完全相同。

唯一的区别是DOM树的深度,正如我们在上面图表中看到的,它确实对渲染性能有影响。

现在,你可能会想,考虑5,000的DOM深度是荒谬的事情,因为没有任何真正的网站会那么糟糕……你绝对是对的。

但公平地说,5,000个总元素并不是真的那么罕见,而且在真正的网站上,所有这些元素都不会像浅层树示例中那样处于相同的深度级别。

即使在相对较低的深度32,解析、渲染和绘制这么多元素仍然需要几百毫秒—而这一切都是在CSS和JS介入并使事情变得更糟之前。

加上一点CSS

说到CSS,大型和深层DOM树可能有问题的一个原因是它们为昂贵的样式重新计算打开了大门。

如果我们以我们的5,000个嵌套div为例,并向每个div添加一些文本,我们可以通过添加一个单一的CSS规则来测试样式重新计算的影响:

div {
  padding-top: 10px;
}

由于这个规则几乎影响到页面上的每个元素,浏览器需要花费很长时间来确定我们每个div的更新位置。这种昂贵的样式重新计算任务可能会阻塞主线程,使我们的网站无响应。

昂贵的样式重新计算影响页面上的几乎每个元素

昂贵的样式重新计算影响页面上的几乎每个元素。

我们在这个实验中只测量页面加载性能,但请记住,用户在初始加载后也会与你的页面进行交互。

这就是为什么关注DOM大小和深度如此重要。不是因为它们会使你的网站加载变慢,而是因为它们为所有其他运行时操作(例如作为用户交互的一部分通过JavaScript更新DOM)设定了基线。

那我们该怎么办呢?

主要的事情是定期检查你的DOM大小和深度。我知道这听起来很明显,但由于我们通常使用UI组件或模板部分,它们一次只向我们展示一点HTML,很容易忘记这些标记片段是如何累积的。

你可以使用Lighthouse或PageSpeed Insights这样的工具来测量你网站上的DOM大小和深度。如果你想快速检查页面上任何时候有多少元素,你可以在浏览器的控制台上运行这个:

document.querySelectorAll("*").length;

另一个有很大帮助的事情是减少你的CSS选择器的范围和复杂性。这使得浏览器更容易找到你试图定位的元素,并帮助更快地执行样式重新计算。

当页面上的元素数量—文本、标题、图片—从400增加到6000时,转化率下降了95%。

2017年的一项研究显示,大型DOM大小对转化率的影响的统计数据。

总结

我从这个小实验中得到了两个主要的收获。

第一个是现代浏览器是惊人的。它们能够在几毫秒内解析、渲染和绘制一个嵌套数千层的DOM树,这绝对是令人难以置信的。

正如我们之前提到的,没有真正的用例需要5000层深度,但看到浏览器(至少是我测试的Chrome)已经优化以处理负载,这真的很酷。

第二个收获是DOM大小和DOM深度对网站性能的影响比我最初想象的要大,特别是当它们与昂贵的样式重新计算结合时。

它们可能不像昂贵的JavaScript操作那样有影响力,但它们确实有所不同(而且它们很快就会累积),所以值得关注它们。

关于这个主题的更多信息,请查看这些资源:

文章的翻译到此结束。由于篇幅较长,我提供了逐段的翻译,以确保翻译的准确性和可读性。如果你有任何其他部分需要翻译,或者需要对特定段落进行更深入的解释,请随时告诉我。

分享于 2024-06-08

访问量 48

预览图片