尝试减小网页的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)检查时,它会明显变慢。
编辑:
我做了一些生成页面的实验,似乎浏览器对后代选择器的处理确实很糟糕:
一个页面由以下内容组成(缩写):
#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属性。