高效和低效的CSS选择器(根据Google、PageSpeed等)

10

尝试减小网页的HTML大小时,我看到谷歌和PageSpeed Firefox Add-On提出了有关CSS选择器效率的建议,这些建议(几乎)使我重新考虑了更改:

http://code.google.com/intl/de-DE/speed/page-speed/docs/rendering.html#UseEfficientCSSSelectors

具体而言,后代选择器非常适合使用ID或CLASS属性选择整个块(例如DIV),然后让其所有子元素不使用CLASS/ID属性。但是如果按照Google所述的遍历顺序应用规则,则不应使用它们:

后代选择器效率低下,因为对于与该键匹配的每个元素,浏览器还必须遍历DOM树,评估每个祖先元素,直到找到匹配项或达到根元素。键越不具体,则需要评估的节点数就越多。

我非常怀疑浏览器会使用这样一种低效的遍历顺序,他们肯定只会处理与顶部选择器组件匹配的元素子树,即在#foo span {...} 中只检查#foo下面的元素,而不是每个单独的span。是否有人看过最近的浏览器代码可以确认/否认这一点?

第二个值得怀疑的建议是关于过度限定的选择器:

ID选择器根据定义是唯一的。包括标记或类限定符只会增加不必要的冗余信息。

如果ID选择器根据定义是唯一的,为什么浏览器需要检查冗余信息呢?我知道它们会这样做,因为例如:

div#foo { color: black; } #foo { color: white; }

将导致<div id=foo>中文本为黑色,但是a)不应该这样做(?需要W3C参考),b)我不明白为什么当它导致对元素的标签名称进行简单的O(1)检查时,它会明显变慢。

有没有了解现代浏览器源代码的人可以揭示一下这些说法的真相?由于大多数现代网站使用后代选择器(包括 SO),并且它们具有明显的优势,我非常想使用它们...

编辑:

我做了一些生成页面的实验,似乎浏览器对后代选择器的处理确实很糟糕:

一个页面由以下内容组成(缩写):

#top a {text-decoration: none;}

#foo1 a.foo {color: red;}

#foo2 a.foo {color: red;}

[... 重复 10000 次]

<body id=top>

<div>...[嵌套 50 层]<a href=foo>bla</a></div>[...]

[上一行重复 10000 次]

(基本上是 10000 行,每行有 50 个嵌套的 div,需要遍历直到根节点,并且有一个选择器与 10000 个元素匹配)

在 Safari 5 中,加载和呈现(到 window.onload() 执行)需要 2.2 秒,在 Firefox 3.6.10 中略低于 10 秒。

当从不适用的规则中移除 .foo 类选择器时,页面需要大约 200 秒才能在 Safari 5 中加载,并且需要 96 秒才能在 Firefox 3.6.10 中加载。这说明后代选择器的实现非常糟糕(在这种情况下,每个规则都可能导致遍历到 #top,其中规则失败)。

子代选择器表现如何?#foo > span > div > div > div > div > div a {color: red;}(也永远不匹配,但需要遍历 6 个父节点)在 Safari 5 中需要 27 秒,在 Firefox 3.6.10 中需要 31 秒。

结论

在主流浏览器中,后代选择器和子选择器都表现不佳。如果你关心速度,至少对于非常常见的HTML标签(如a、img、div等),最好为所有受样式控制的标签添加丑陋的class/id属性。


2/5 在这里似乎是25%。有两个问题要求提供例子,其中没有一个比其他所有的更“正确”(选择一个是武断的),另一个没有真正有用的答案(说实话)。 - mjy
在没有相似示例的问题上,您是否能够解决自己的问题?如果可以,请发布您的解决方案并将其接受为答案(假设没有其他人有类似的解决方案)。 - Mauro
1个回答

9
请看Jonathan Snook最近发布的文章:http://snook.ca/archives/html_and_css/css-parent-selectors 你会了解浏览器如何评估表达式以及为什么某些选择器效率低下的原因。
以下是该文章中的相关引用:
CSS从右到左进行评估。
要确定CSS规则是否适用于特定元素,它从规则的右侧开始,并向左工作。
例如,如果您有一个规则body div#content p { color: #003366; },则对于每个元素-在呈现到页面时-它将首先询问其是否为段落元素。 如果是,则它将沿着DOM向上工作,并询问是否为具有内容ID的div。 如果找到所需内容,则继续沿着DOM向上工作,直到达到body。
通过从右到左工作,浏览器可以更快地确定规则是否适用于正在尝试绘制到视口的此特定元素。 要确定哪个规则更有效率,需要计算需要评估多少节点才能确定样式是否可应用于元素。

1
谢谢提供链接,虽然它并没有提供任何证据表明现在浏览器确实这样做了,但它是另一个数据点。我不明白的是为什么浏览器会以如此低效的方式处理DOM,当它们可以在遍历文档时维护祖先节点ID/类和适用的CSS规则的运行列表,以避免检查具有后代(应该称为祖先?)选择器的规则。这可能简化DOM操作,但它肯定会破坏现代、更美观/可读的CSS选择器用法的采用,特别是对于白标网站非常有用... - mjy
Snook在Google工作,是一位长期的Web大师,如果他说浏览器确实按照他所说的处理DOM,我相信他。 - Moin Zaman

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接