HTML DOM 是如何影响性能的?

你用 Lighthouse 测量过网站性能吗?你可能见过“避免过多的 DOM 节点”的警告,就像下面这样:

aadaf6ace6bc186b781978023b19bb4d.png
image.png

Lighthouse 提醒我们,DOM 节点太多会增加内存使用量,还会导致样式计算很耗时。再加上网站上其他各种操作,这会显著影响用户体验,尤其是那些使用低端设备的用户。

前几天,我在看自己网站的性能报告时,就注意到了这个警告。不过,吸引我注意的不是 DOM 元素的总数,而是它下面一个指标:最大 DOM 深度。

上周我写周报时,花了很多时间研究树形数据结构,所以时间复杂度和树的深度之间的关系我还记得很清楚。看到这个指标,我马上就想:

🤔 DOM 深度会影响渲染性能吗?

我们都知道,DOM 是树状结构,树的深度会影响查找元素的速度。看看下面这两棵 DOM 树:

d7b6120f6f524de20510787a945e696c.png
image.png

两棵树的元素数量相同,但深度(或者说高度)不同,一棵是 2,另一棵是 6。树越深,访问其中的元素就需要更多操作。

举个例子,假设我们要从根节点开始访问 元素。在浅树上,只需要两步:找到 的子元素数组,然后访问索引 4 处的子元素:

body.children[4];

但在深树上,需要六步才能找到同一个元素:

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

当我们处理二叉搜索树 (BST) 这类数据结构时,树的高度就很重要。为了保证查找等操作的效率,很多数据结构都实现了自平衡 BST,以便在树增长时保持其高度最小。

回到 DOM,理论上,树越深,速度就越慢。但实际情况如何呢?

我做了个小实验来验证一下。

一个小实验

我创建了两个 HTML 页面,只包含三行文字和 100 个空 div。唯一的区别是,一个页面将所有 div 直接放在文档的 body 中,另一个页面则将 div 嵌套起来。

浅元素树的页面像这样:

<html>
  <body>
    <div></div>
    <div></div>
    <div></div>
    <div></div>
    <!-- 95 divs later... -->
    <div>This is the last of 100 divs.</div>
  </body>
</html>

而深树的页面像这样:

<html>
  <body>
    <div>
      <div>
        <div>
          <div>
            <!-- 95 divs later... -->
            <div>This is the last of 100 divs.</div>
          </div>
        </div>
      </div>
    </div>
  </body>
</html>

我用 Chrome 浏览器运行了性能测试,结果第一个页面的总加载时间(解析 + 渲染 + 绘制)是 51 毫秒,而第二个页面的加载时间是 53 毫秒。

这结果有点出乎意料,差别竟然这么小!

当然,这个例子不太严谨,因为所有 div 都是空的,浏览器只需要渲染几行文本。但我还是期待看到更大的差异。

幸好,编程的魅力就在于此,我只需改几行代码,就能测试更多 div 的情况。我测试了 200 个、300 个、400 个……一直到 500 个 div 时,性能差异终于明显了。

16a0a8dc0377d997232d053228c47dcd.png
image.png

浅树示例在 56 毫秒内渲染了所有 500 个 div,几乎不受 HTML 数量增加 5 倍的影响。但现在嵌套树需要 102 毫秒,几乎是两倍!

我继续测试,最多测试了 5,000 个 div,结果如下:

255875f62d680c5ba7f52d359317eb68.png
image.png
DOM 元素浅树加载时间 (毫秒)嵌套树加载时间 (毫秒)
1005153
2005558
3005574
4005887
50056102
60062116
70066145
80078151
90077165
1,00083189
2,000101339
3,000121509
4,000142667
5,000166844

值得注意的是,浅树页面和嵌套树页面的大小相同,都包含相同数量的元素,在浏览器上渲染的结果也完全一样。

唯一的区别就是 DOM 树的深度,从上面的表格中我们可以看到,它确实会影响渲染性能。

你可能会说,5,000 的 DOM 深度太夸张了,现实中根本不会有网站这么糟糕……没错,你说得对!

但 5,000 个元素其实很常见,而且真实的网站中,这些元素不会像浅树示例那样都处于同一深度。

即使深度只有 32,解析、渲染和绘制这么多元素仍然需要几百毫秒——这还是在 CSS 和 JS 加载之前。

添加一些 CSS

说到 CSS,大型和深层 DOM 树之所以会出问题,是因为它们会导致耗时的样式重新计算。

如果我们给 5,000 个嵌套 div 的例子添加一些文字,然后添加一条 CSS 规则,就能测试样式重新计算的影响:

div {
 padding-top: 10px;
}

这条规则几乎影响页面上的每个元素,所以浏览器需要很长时间才能计算出每个 div 的更新位置。这种耗时的样式重新计算会阻塞主线程,导致网站无响应。

b91d710c970dac654c007d285705e4ff.png
image.png

在这个实验中,我们只测量了页面加载性能,但别忘了用户还会在页面加载完成后与页面交互。

所以,关注 DOM 大小和深度非常重要。它们不仅会影响网站加载速度,还会影响所有运行时的操作,比如用户交互时通过 JavaScript 更新 DOM。

那我们该怎么办呢?

最重要的是,定期检查你的 DOM 大小和深度。  我知道这听起来很平常,但我们平时写代码时,通常只关注一小部分 HTML,很容易忽略这些代码片段加起来会有多大。

可以使用 Lighthouse 或 PageSpeed Insights 等工具来测量 DOM 大小和深度。如果你想快速检查当前页面有多少个元素,可以在浏览器的控制台中运行以下代码:

document.querySelectorAll("*").length;

另一个有用的技巧是 减少 CSS 选择器的范围和复杂性。这可以让浏览器更容易找到你想要定位的元素,从而更快地执行样式重新计算。

db268de14c042eb9e41babcf57ab2daa.png
image.png

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

总结

这个小实验让我明白了两件事。

第一,现代浏览器真的很强大! 它们可以在几毫秒内解析、渲染和绘制一个嵌套了数千层的 DOM 树,这太不可思议了。

当然,现实中很少有网站需要 5,000 层的深度,但看到浏览器已经针对这种情况进行了优化,还是让人觉得很安心。

第二,DOM 大小和深度对网站性能的影响比我想象的要大,尤其是与耗时的样式重新计算结合在一起时。

它们的影响力可能不如耗时的 JavaScript 操作那么大,但它们确实会产生影响(而且会很快累积),所以值得关注。

关于这个主题,还有很多值得学习的资料:

  • 过多的 DOM 节点如何影响交互性,以及你能做些什么[1]

  • 减少样式计算的范围和复杂性[2]

  • 避免过多的 DOM 深度[3]

  • 避免过多的 DOM 节点[4]

参考资料

[1]

过多的 DOM 节点如何影响交互性,以及你能做些什么: https://web.dev/articles/dom-size-and-interactivity

[2]

减少样式计算的范围和复杂性: https://web.dev/articles/reduce-the-scope-and-complexity-of-style-calculations

[3]

避免过多的 DOM 深度: https://sitebulb.com/hints/performance/avoid-excessive-dom-depth/

[4]

避免过多的 DOM 节点: https://developer.chrome.com/docs/lighthouse/performance/dom-size

最后,别忘了还有更多精彩内容等你探索!


往期推荐

38个Vue、Nuxt 和 Vite 技巧、窍门和实践的合集

10 个 必备的JavaScript 实用技巧和最佳实践!

Vue 开发者必备技能:深入理解 Composition API !

2024 年 10 个很实用的 CSS 新特性,你不一定知道!

10个 React 开发避坑指南

12 种 Vue 设计模式

CSS 秘密武器:25 个小技巧,助你写出更优雅的代码!

JavaScript 的新技能:5 大技巧,打造更强大的 Web 应用

Vue 3 将推出新特性,可以抛弃虚拟DOM了!

我是前端宝哥,每日分享前端开发技术,关注下面二维码,围观我的朋友圈。

b80f4525ae7cb86768fe37fa3899f671.png

关注下方公众号加星标,送我的电子书资料

  • 回复「小抄」,领取Vue、JavaScript 和 WebComponent 小抄 PDF

  • 回复「Vue脑图」获取 Vue 相关脑图

  • 回复「思维图」获取 JavaScript 相关思维图

  • 回复「简历」获取简历制作建议

  • 回复「简历模板」获取精选的简历模板

  • 回复「电子书」下载我整理的大量前端资源,含面试、Vue实战项目、CSS和JavaScript电子书等。

  • 回复「知识点」下载高清JavaScript知识点图谱

  • 回复「读书」下载成长的相关电子书

cbeb57a9dd4a55f3eeab14b2813e5e6b.png

PS:感谢您读到这里。原创更文不易,请关注我,加个星标。文章末尾点赞+分享,您的认同,将是我前行的最大动力!

  • 14
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值