原文链接Does shadow DOM improve style performance$$^{[1]}$$,翻译在2021年10月9日。本译文也收录在公众号[[大前端从入门到跑路]](https://mp.weixin.qq.com/s/wxYkp_DcxW52YTKhj6P8yA "[大前端从入门到跑路]")。 我的一个简短的回答是:能提高一点。虽然它可能还不足以在一般的 Web APP 中产生很大的差异,但我们值得去理解为什么它能提高样式性能。 首先,让我们回顾一下浏览器的渲染流程 $$^{[2]}$$ (`rendering pipeline`),以及为什么我们可以推测 `shadow DOM` 能提高样式性能。我们知道,浏览器渲染过程的两个基本部分是 **样式计算** (`style calculation`)和 **布局计算** (`layout calculation`),或者简称为“样式”和“布局”。前者的工作是:确定哪些 DOM 节点具有哪些样式(基于 CSS),后者则是:确定这些 DOM 节点实际放置在页面上的位置(使用上一步中计算出的样式)。  Chrome DevTools 中的性能跟踪,显示了基本的 JavaScript → 样式(Style) → 布局(Layout) → 绘制(Paint)流程 浏览器很复杂,但一般来说,页面上的 DOM 节点和 CSS 规则越多,运行样式和布局步骤所需要的时间就越长。提高此过程性能的一个方法是将工作分解为更小的块,即封装(`encapsulation`)——这是 shadow DOM 能提高样式性能的原因。 对于布局(`Layout`)的封装,我们有 CSS Containment $$^{[3]}$$ 。这个在其他文章 $$^{[4]}$$ 里已经讲过了,这里就不赘述了。可以这么说,我认为有足够的证据表明 `CSS containement`可以提高性能,所以如果你还没有尝试将 `contains: content` 置放在 `UI`里面以查看它是否可以提高布局性能,那么你绝对应该去试一试。 至于样式(`Style`)的封装,我们有一些新奇玩意儿:shadow DOM $$^{[5]}$$ 。就像 `CSS Containment`能提高布局性能一样,`shadow DOM`理论上 应该也能提高样式性能。让我们考虑一下原因: ## 什么是样式计算 如前文所述,样式计算不同于布局计算。布局计算与页面的几何布局有关,而样式计算则更明确地与 CSS 相关。这是采用如下规则的过程: ``` div > button { color: blue; } ``` 还有一个 DOM 树: ``` ``` ...例子如上,我们可以确定 ``应该具有 `color:blue`,因为它的父元素是 ``。粗略地说,这是计算 CSS 选择器(在本例中为 `div > button`)的过程。 现在,在最坏的情况下,这是一个 时间复杂度为 $$O(n * m)$$ 操作,其中 n 是 DOM 节点的数量,m 是 CSS 规则的数量。(即遍历每个 DOM 节点和每个 CSS 规则,弄清楚它们是否相互匹配。)显然,浏览器不会这样做,否则任何大小合适的 Web 应用程序都会变得非常缓慢。浏览器在这方面有很多优化,这也是我们经常建议不要过分担心 CSS 选择器性能的部分原因。(请参阅这篇文章 $$^{[6]}$$ ,了解该主题的最新处理) 也就是说,如果你使用过有着大量 CSS 代码量的代码库,你可能会注意到,在 Chrome 性能配置文件中,样式成本不是零。根据 CSS 的大小或复杂程度,你可能会发现实际上花在样式计算上的时间多于布局计算。因此,研究样式性能并不是完全没有价值的。 ## Shadow DOM 和样式计算 为什么 `shadow DOM` 会提高样式性能?这里再说一次,是因为封装!如果你有一个包含 1,000 个规则的 CSS 文件和一个包含 1,000 个节点的 DOM 树,浏览器在运行前,并不会知道哪些规则适用于哪些节点。即便你使用 CSS Modules $$^{[7]}$$ 、Vue scoped CSS$$^{[8]}$$ 或 Svelte scoped CSS$$^{[9]}$$ 来创作你的 CSS,最终你也只会得到一个隐式映射到 DOM 树的样式表,因此浏览器必须在运行时找出 CSS 和 DOM 树之间的联系(例如使用类或属性选择器)。`Shadow DOM` 则不同。使用 shadow DOM,浏览器不必猜测哪些规则适用于哪些节点——因为它就在 DOM 节点中: ``` #shadow-root #shadow-root ``` 在这种情况下,浏览器不需要针对 DOM 中的每个节点测试 `div {color: green} `规则——它知道它的范围是 ``。`` 中的 `div {color: blue}`规则同上。理论上,这可以加快样式计算过程,因为浏览器可以依赖于通过 shadow DOM 的显式范围,而不是通过类或属性的隐式范围。 ## 基准测试 这是理论,但当然,在实践中事情总是更复杂。所以我整理了一个 benchmark$$^{[10]}$$ 来衡量 `shadow DOM`的样式计算性能。某些 CSS 选择器往往比其他选择器更快,因此为了获得不错的覆盖率,我测试了以下选择器: * id (`#foo`) * class (`.foo`) * attribute (`[foo]`) * attribute value (`[foo="bar"]`) * “silly” (`[foo="bar"]:nth-of-type(1n): last-child:not(:nth-of-type(2n)):not(:empty)`) 粗略地说,我希望 `id` 和 `class` 选择器是最快的,其次是 `attribute`和 `attribute value`选择器,然后是 `“silly”`选择器(添加一些东西以真正让样式引擎工作)。 为了测量,我使用了一个简单的 requestPostAnimationFrame$$^{[11]}$$ 插件,它可以测量在样式、布局和绘制上所花费的时间。这是 `Chrome DevTools` 中正在测量的内容的屏幕截图(注意 `Timings` 部分下的 `“total”`):  为了运行实际的基准测试,我使用了 Tachometer $$^{[12]}$$ ,这是一个很好的浏览器微基准测试工具。在这种情况下,我只取了 51 次迭代测试中的中位数。 基准测试创建了几个自定义元素,并且要么使用自己的 ` 2 条评论 fzgppzyufa February 28th, 2025 at 07:34 pm 反驳对手观点时需更注重逻辑严密性。 回复 nrwbwrbiya January 6th, 2025 at 02:39 pm 哈哈哈,写的太好了https://www.lawjida.com/ 回复 发表评论 取消回复 评论 * 私密评论 名称 * 邮箱 * 地址 发表评论 提交中...