菜单

CSS选择器渲染效率,编写高效的

2019年11月28日 - 银河官方网站
CSS选择器渲染效率,编写高效的

编写高效的 CSS 选择器

2013/03/08 · CSS · 4
评论 ·
CSS

伯乐在线注:我们昨日在@程 序员的那些事
微博上推荐了英文原文,感谢@freestyle21
和@沈涛-WEB工程师 的热情参与。

 

高效的CSS已经不是一个新的话题了,也不是我一个非得重拾的话题,但它却是我在Sky公司工作之时,所感兴趣的,关注已久的话题。

有很多人都忘记了,或在简单的说没有意识到,CSS在我们手中,既能很高效,也可以变得很低能。这很容易被忘记,尤其是当你意识到你会的太少,CSS代码效率很低的时候。

下面的规则只真正被应用到那些速度要求很高,有成百上千的DOM元素被绘制在页面上的大型网站。但是,实践出真理,这和你是在创建下一个Facebook,还是写一个本地的展示页面都没有关系,多知道一点总是好的。

1
浏览器如何识别你的选择器

CSS选择器:

对我们大多数人来说,CSS选择器并不陌生。最基本的选择器是元素选择器(比如div),ID选择器(比如#header)还有类选择器(比如.tweet)。

一些的不常见的选择器包括伪类选择器(:hover),很多复杂的CSS3和正则选择器,比如:first-child,class
^= “grid-”.

CSS选择器具有高效的继承性,引用Steve Souders的话,
CSS选择器效率从高到低的排序如下:

  1. ID选择器 比如#header
  2. 类选择器 比如.promo
  3. 元素选择器 比如 div
  4. 兄弟选择器 比如 h2 + p
  5. 子选择器 比如 li > ul
  6. 后代选择器 比如 ul a 7. 通用选择器 比如 *
  7. 属性选择器 比如 type = “text”
  8. 伪类/伪元素选择器 比如 a:hover

以上引用自Steve Souders的Even Faster网站、

我们不得不提的是,纵使ID选择器很快、高效,但是它也仅仅如此。从Steve
Souders的CSS
Test我们可以看出ID选择器和类选择器在速度上的差异很小很小。

在Windows系统上的Firefox 6上,我测得了一个简单类选择器的(reflow
figure)重绘速度为10.9ms,而ID选择器为12.5ms,所以事实上ID比类选择器重绘要慢一点点。

ID选择器和类选择器在速度上的差异基本上没有关系。

在一个标签选择器(a)的测试上显示,它比类或ID选择器的速度慢了很多。在一个嵌套很深的后代选择器的测试上,显示数据为440左右!从这里我们可以看出ID/类选择器
和 元素/后代选择器中间的差异较大,但是相互之间的差异较小。

注意:
这些数据可能在不同计算机和浏览器中间的差异较大。强烈地建议大家在自己的机子上测试一下。

首先我们需要清楚,浏览器是如何读取选择器,以识别样式,并将相应的样式附于对应的HTML元素,达到美化页面的效果。Chris
Coyier曾在《Efficiently Rendering
CSS》一文中说过“浏览器读取你的选择器,遵循的原则是从选择器的右边到左边读取。换句话说,浏览器读取选择器的顺序是由右到左进行”。比如说:

组合选择器

你可以有一个标准的选择器比如
#nav,来选择任何带有ID为”nav”的元素,或在你可以有一个组合选择器比如#nav
a,来选择任何在ID为’nav’的元素里面的链接元素

此刻,我们读这些是从左到右的方式。我们是先找到#nav,然后从它的里面找其他元素。但是浏览器解析这些不是这样的:浏览器解析选择器是从右到左的方式。

在我们看来,#nav里面带了一个a,浏览器却是看到的a在#nav里面。这些细微的差异对选择器的效率有很大的影响,同时学这些差异也是很有价值的。

如果想要知道更多浏览器这样解析的原因,请看Stack
Overflow上的讨论

浏览器从最右边的元素开始(它想要渲染的元素),然后用它的方式回溯DOM树比从DOM树的最高层开始选择向下寻找,甚至可能达不到最右边的选择器—关键的选择器要高效。

这些对CSS选择器的效率有很大的影响。

div.nav
< ul li a[title]

关键选择器

关键选择器,正如前面讨论的一样,是一个复杂的CSS选择器中最右边部分。它是浏览器最先寻找的。

现在我们回到讨论开始的地方,哪类选择器是最高效的?哪个是会影响选择器效率的关键选择器;写CSS代码的时候,关键选择器是能否高效的决定因素。
一个关键CSS选择器像这样:

CSS

#content .intro {..}

1
#content .intro {..}

是不是高效选择器比如类选择器天生就高效?浏览器会寻找.intro的实例(可能会很多),然后沿着DOM树向上查找,确定刚才找到的实例是否在一个带有ID为”content”的容器里面。

但是,下面的选择器就表现的不是那么好了:

CSS

#content * {..}

1
#content * {..}

这个选择器所做的是选择所有在页面上的单个元素(是每个单个的元素),然后去看看它们是否有一个
#content
的父元素。这是一个非常不高效选择器因为它的关键选择器执行开销太大了。

运用这些知识我们就可以在分类和选择元素的时候做出更好的选择。

假设你有一个复杂的页面,它相当巨大并且在你的一个很大很大的站点上。在那个页面上有成百上千甚至上万的
a
标签。它还有一个小的社交链接区域放在一个ID为#social的Ul里面。我们假设它们是Twitter,Facebook,Dribbble还有
Google+的链接吧。在这个页面上我们有四个社交链接和成百上千的其他链接。
下面的这个选择器就自然的不是那么高效和合理了:

CSS

#social a {…}

1
#social a {…}

这里发生的情况是浏览器会在定位到#social区域下的四个链接之前得到页面上所有成千上万的链接。我们的关键选择器匹配了太多我们不感兴趣的其他元素。

为了补救我们可以给每个在社交链接区域的 a 增加一个更特殊、明确的选择器
.social-link ,
但是这好像有点违背我们的认知:当我们能用组合选择器的时候就不要放不必要的类标示在元素上。

这就是为什么我对选择器的性能如此感兴趣的原因了:必须在web
标准最佳实践和速度之间的保持平衡。

通常我们有:

CSS

<ul id=”social”> <li><a href=”#”
class=”twitter”>Twitter</a></li> <li><a
href=”#” class=”facebook”>Facebook</a></li>
<li><a href=”#”
class=”dribble”>Dribbble</a></li> <li><a
href=”#” class=”gplus”>Google+</a></li> </ul>

1
2
3
4
5
6
<ul id="social">
    <li><a href="#" class="twitter">Twitter</a></li>
    <li><a href="#" class="facebook">Facebook</a></li>
    <li><a href="#" class="dribble">Dribbble</a></li>
    <li><a href="#" class="gplus">Google+</a></li>
</ul>

CSS:

CSS

#social a {}

1
#social a {}

我们现在最好有:

XHTML

<ul id=”social”> <li><a href=”#” class=”social-link
twitter”>Twitter</a></li> <li><a href=”#”
class=”social-link facebook”>Facebook</a></li>
<li><a href=”#” class=”social-link
dribble”>Dribbble</a></li> <li><a href=”#”
class=”social-link gplus”>Google+</a></li> </ul>

1
2
3
4
5
6
<ul id="social">
    <li><a href="#" class="social-link twitter">Twitter</a></li>
    <li><a href="#" class="social-link facebook">Facebook</a></li>
    <li><a href="#" class="social-link dribble">Dribbble</a></li>
    <li><a href="#" class="social-link gplus">Google+</a></li>
</ul>

加上CSS:

CSS

#social .social-link {}

1
#social .social-link {}

这个新的关键选择器将会匹配更少的元素,这意味着浏览器能够很快的找到它们并渲染特定的样式,然后专注于下一件事。

另外,事实上我们可以用.social-link{}更清晰的选择,而不是过分限制它。阅读下一部分你会原因…

简单的重述一次,你的关键选择器会决定浏览器的工作量,因此,我们应该重视一下关键选择器

上面的实例来说,浏览器首先会尝试在你的HTML标签中寻找“a[title]”元素,接着在匹配“li和ul”,最后在去匹配“div.nav”。这就是前成所主的“选择器从右到左的原则”。

过度限制选择器

现在我们知道了什么是关键选择器,还有它是大部分工作的来源,但是我们可以更乐观一点。拥有一个明确的关键选择器最大的好处就是你可以避免使用过度限制选择器。一个过度限制选择器可能像:

CSS

html body .wrapper #content a {}

1
html body .wrapper #content a {}

这里的写的太多了,至少3个选择器是完全不需要的。它可以最多像这个样子:

CSS

#content a {}

1
#content a {}

这会发生什么呢? 首先第一个意味着浏览器不得不寻找所有的 a
元素,然后检查他们是否在一个ID为”content”的元素中,然后如此循环直到HTML标签。这样造成了太多的我们不太想要的花费。了解了这个,我们得到一些更现实的例子:

CSS

#nav li a{}

1
#nav li a{}

变成这个:

CSS

#nav a {}

1
#nav a {}

我们知道如果a在li里面,它也必定在#nav里面,所有我们可以马上把li从选择器组中拿掉。然后,既然我们知道在页面中只有一个ID为nav的元素,那么它依附的元素就是完全没有关系得了,我们也可以拿掉ul

过度限制选择器使浏览器工作比它实际需要的更繁重,花费的时间更多。我们可以删掉不必需的限制,来使我们的选择器更简单和高效。

选择器的最后一部分,也就是选择器的最右边(在这个例子中就是a[title]部分)部分被称为“关键选择器”,它将决定你的选择器的效率如何?是高还是低。

这些真的需要吗?

最短的答案是:或许不是。

最长的答案是:它取决于你正在搭建的站点。如果你正在为你的晋升而努力,那么就好好写出简单、高效的CSS代码吧,因为你可能不会感觉到它给你带来的改变。
如果你正在搭建下一个每个页面都以毫秒计算的Amazon网站,这样有时速度会很快,但有时可能不是。

浏览器将会在解析CSS的速度上变得更好,甚至在手机端。在一个网站上,你不太可能会觉察到一个低效的CSS选择器,但是….

那么如何让关键选择器更有效,性能化更高呢?其实很简单,主要把握一点“越具体的关键选择器,其性能越高”。

但是

它确实发生了,浏览器还是不得不去做我们讨论的所有工作,无论它们变得多快。即使你不需要或者甚至不想实践任何一个,但是它都是我们值得学习的知识。请记住选择器可能会让你付出很大代价,你应该避免盯着一个看。这意味着如果你发现你自己在写像这样的:

CSS

div:nth-of-type(3) ul:last-child li:nth-of-type(odd) *{
font-weight:bold }

1
div:nth-of-type(3) ul:last-child li:nth-of-type(odd) *{ font-weight:bold }

这时,你可能就做错了。

现在,在高效选择器的世界我还是一个新人。所以如果我忘记了什么,或者你有需要补充的,请在评论里面留言。

 

更多高效选择器

我还不能完全介绍Steve
Souders的网站和书籍(《更快速网站》、《高性能网站》),它们是如此之好,以至于值得你花更多时间来阅读和推荐。这个家伙只有他自己才了解自己!

图片 1图片 2

 

 

英文原文:Writing efficient CSS
selectors,编译:@freestyle21
和@沈涛-WEB工程师

译文链接:

【非特殊说明,转载必须在正文中标注并保留原文链接、译文链接和译者等信息,谢谢合作!】

赞 3 收藏 4
评论

图片 3

2
CSS选择器的效率

果你阅读了本站的有关于选择器类型的介绍的话,你对选择器并不会感到陌生。就算你没读过,我想CSS选择器不会让我们觉得是新东西,比如我们常用的基本选
择器“元素标签选择器div”、“id选择器#header”、“类选择器.class”,或者说我们很少见的伪类选择器“:focus”以及更复杂的
css3选择器“:nth-child”等等。
选择器有一个固有的效率,我们来看Steve
Souders给排的一个顺序:
id选择器(#myid)
类选择器(.myclassname)
标签选择器(div,h1,p)
相邻选择器(h1+p)
子选择器(ul
> li)
后代选择器(li
a)
通配符选择器(*)
属性选择器(a[rel=”external”])
伪类选择器(a:hover,li:nth-child)
上面九种选择器的效率是从高到低排下来的,基中ID选择器的效率是最高,而伪类选择器的效率则是最低。

我们不得不提的是,纵使ID选择器很快、高效,但是它也仅仅如此。从Steve
Souders的CSS Test我们可以看出ID选择器和类选择器在速度上的差异很小很小。

我们知道ID’s
是最高效的选择器。当你想让渲染速度最高效时,你可能会给每个独立的标签配置一个ID,然后用这些ID写样式。那会超级快,也超级荒唐。这样的结果是语义
极差,维护难到了极点。即使在核心部分你也不应该见过这样做的。我认为这个可以提醒我们不要为了高效的CSS放弃语义和可维护性。

 

3
书写规范

A
不要用标签修饰ID

死也不要像下
面这样干:

ul#main-navigation
{ }

ID’s
是唯一的,所以不需要用标签修饰,这只会让它更低效。

如果你可以避免的话,也不要用它修饰
class 。class
不是唯一的,所以理论上你可以把它用在不同的标签。如果你愿意的话,你可以用标签控制不同的样式,这样你可能需要标签修饰(比如:li.first),但
这样做的人很少,所以,don’t .

B
绝对没有比用后代选择器更糟糕的做法了

David
Hyatt:
后代选择器是CSS里最昂贵的选择器,昂贵得可怕——特别是当它放在标签和通用符后面时。
就如下面这个东东一样,绝对的效
率毒瘤:

html
body ul li a { }

C
一个选择器渲染失败比这个选择器被渲染更高效

我不是很确定是否有更好的证据去证明这一点,因为如果你有大量的选择
器在CSS样式表里无法找到,这样的事情貌似很离奇,但一点必需注意的是,从右到左的解释一个选择器来说,一旦它找不到,那它就会停止尝试。然而如果它找
到了,那它就需要花更多精力去解释了。

D
试想一下为何你这样写选择器

思考
下这东东:

#main-navigation
li a { font-family: Georgia, Serif; }

你可能不需要从
a 选择器开始(如果你只是想换个字体)。下面这个可能更高效些:

#main-navigation
{ font-family: Georgia, Serif; }

E
不要过度限制选择器

 拥有一个明确的关键选择器最大的好处就是你可以避免使用过度限制选择器。一个过度限制选择器可能像:

html
body .wrapper #content a {}

这里的写的太多了,至少3个选择器是完全不需要的。它可以最多像这个样子:

#content
a {}

一些更现实的例子:#nav
li a{}变成这个:#nav a {}

我们知道如果a在li里面,它也必定在#nav里面,所有我们可以马上把li从选择器组中拿掉。然后,既然我们知道在页面中只有一个ID为nav的元素,那么它依附的元素就是完全没有关系得了,我们也可以拿掉ul

过度限制选择器使浏览器工作比它实际需要的更繁重,花费的时间更多。我们可以删掉不必需的限制,来使我们的选择器更简单和高效。

 

4
案例详解:

假设你有一个复杂的页面,它相当巨大并且在你的一个很大很大的站点上。在那个页面上有成百上千甚至上万的
a
标签。它还有一个小的社交链接区域放在一个ID为#social的Ul里面。我们假设它们是Twitter,Facebook,Dribbble还有
Google+的链接吧。在这个页面上我们有四个社交链接和成百上千的其他链接。
下面的这个选择器就自然的不是那么高效和合理了:#social a {…}

这里发生的情况是浏览器会在定位到#social区域下的四个链接之前得到页面上所有成千上万的链接。我们的关键选择器匹配了太多我们不感兴趣的其他元素。

为了补救我们可以给每个在社交链接区域的
a 增加一个更特殊、明确的选择器 .social-link ,
但是这好像有点违背我们的认知:当我们能用组合选择器的时候就不要放不必要的类标示在元素上。

这就是为什么我对选择器的性能如此感兴趣的原因了:必须在web
标准最佳实践和速度之间的保持平衡。

<ul id=``"social"``>

``<li><a href=``"#"
class=``"twitter"``>Twitter</a></li>

``<li><a href=``"#"
class=``"facebook"``>Facebook</a></li>

``<li><a href=``"#"
class=``"dribble"``>Dribbble</a></li>

``<li><a href=``"#"
class=``"gplus"``>Google+</a></li>

</ul>

CSS:#social
a {}

改变后:

<``ul
id``=``"social"``>

``<``li``><``a
href``=``"#"
class``=``"social-link twitter"``>Twitter</``a``></``li``>

``<``li``><``a
href``=``"#"
class``=``"social-link facebook"``>Facebook</``a``></``li``>

``<``li``><``a
href``=``"#"
class``=``"social-link dribble"``>Dribbble</``a``></``li``>

``<``li``><``a
href``=``"#"
class``=``"social-link gplus"``>Google+</``a``></``li``>

</``ul``>

CSS:#social
.social-link {}

这个新的关键选择器将会匹配更少的元素,这意味着浏览器能够很快的找到它们并渲染特定的样式,然后专注于下一件事。

另外,事实上我们可以用.social-link{}更清晰的选择,而不是过分限制它。

 

相关文章

发表评论

电子邮件地址不会被公开。 必填项已用*标注

网站地图xml地图