css选择符的渲染效率

摘要:
作为网站的前端开发工程师,您应该避免编写一些常见的昂贵的CSS选择器,并尝试编写高效的CSS选择器以加快页面呈现速度并缩短页面呈现时间。事实上,在所有浏览器中,使用class和id进行渲染比使用同级选择器、后代选择器和子选择器来提高页面性能更值得注意。根据上面的顺序,我们清楚地知道ID和类名对于键选择器是最有效的,而CSS3的伪类和属性选择器虽然使用方便,但效率最低。

  CSS选择符由一些初始化参数组成,这些参数指明了要应用这个CSS规则的页面元素。作为一个网站的前端开发工程师,应该避免编写一些常见的开销很大的CSS选择符模式,尽量编写高效的CSS选择符,从而加快页面的渲染速度,缩短页面呈现时间。

  我们先来看一下safari和webkit的架构师David Hyatt的两段话:

  1. 样式系统从最右边的选择符开始向左进行匹配规则。只要当前选择符的左边还有其他选择符,样式系统就会继续向左移动,直到找到和规则匹配的元素,或者因为不匹配而退出。
  2. 如果你非常在意页面的性能那千万别使用CSS3选择器。实际上,在所有浏览器中,用 class 和 id 来渲染,比那些使用同胞,后代选择器,子选择器(sibling, descendant and child selectors)对页面性能的改善更值得关注。

  Google 资深web开发工程师Steve Souders对CSS选择器的效率从高到低做了一个排序:

  1.id选择器(#myid)

  2.类选择器(.myclassname)

  3.标签选择器(div,h1,p)

  4.相邻选择器(h1+p)

  5.子选择器(ul < li)

  6.后代选择器(li a)

  7.通配符选择器(*)

  8.属性选择器(a[rel="external"])

  9.伪类选择器(a:hover,li:nth-child)  

  上面九种选择器中ID选择器的效率是最高,而伪类选择器的效率则是最底。详细的介绍大家还可以点击Writing efficient CSS selectors

  综合上面的顺序,我们清楚的知道,id和类名用于关键选择器上效率是最高的,而CSS3的仿伪类和属性选择器,虽然使用方便,但其效率却是最低的

知道了基本原理以后,我们编写CSS时就应该注意了。下面举几个例子,让大家理解的更透彻一些:

1.不要在编写id规则时用标签名或类名

  1. BAD
  2. button#backButton {…}
  3. BAD
  4. .menu-left#newMenuIcon {…}
  5. GOOD
  6. #backButton {…}
  7. GOOD
  8. #newMenuIcon {…}

  由于样式系统从最右边的选择符开始向左进行匹配,只要当前选择符的左边还有其他选择符,样式系统就会继续向左移动,直到找到和规则匹配的元素,或者因为不匹配而退出,所以,在button#backButton {…}中,样式系统先找到id为backButton的元素,然后继续向左匹配,查看该元素的标签名是不是button,如果不是,查找下一个id为backButton的元素,再检查这个元素的标签名,如此周而复始,直到到达文档末尾。如果只是#backButton {…},则样式系统找到id为backButton的元素后,因为左边没有其他选择符了,它就退出而结束查找了。

  另外,根据HTML规范,id在HTML中是唯一的,也就是说一个HTML页面只限定有一个id为“XX”的元素,而不限制拥有这个ID元素的标签名,所以,在button#backButton {…}中,button标签完全是无意义的,可以,而且应该去掉,button#backButton {…}与#backButton {…}是等价的。在#backButton前多写了button,只会引起样式系统向左移动,继续查找页面元素,损耗页面性能,延长页面渲染时间。

  另一方面,在id元素前面添加标签名,还会引起另一个致命的问题,比如原来id为backButton的标签名是button,如果原来样式声明写成button#backButton {…},则现在需要把button标签改成input,id不变,则button#backButton {…}声明的样式规则在这个id同样为backButton的input元素上不起作用,不信大家可以自己动手编写试一下。

2.不要在编写class规则时用标签名

  1. BAD
  2. treecell.indented {…}
  3. GOOD
  4. .treecell-indented {…} //语言化和标签名绑在一起 假设treecell
  5. BEST
  6. .hierarchy-deep {…} //语言化和标签名无关

原理参考第一条。

3.把多层标签选择规则用class规则替换,减少css查找

  1. BAD
  2. treeitem[mailfolder="true"] > treerow > treecell {…}
  3. GOOD
  4. .treecell-mailfolder {…}

4.避免使用子选择器

现在我们来看看David Hyatt的第3段话:后代选择器在CSS中是最昂贵的选择器。贵得要命——尤其是把它和标签或通配符放在一起!

  1. BAD
  2. treehead treerow treecell {…}
  3. BETTER, BUT STILL BAD (see next guideline)
  4. treehead > treerow > treecell {…}

标签后面最好永远不要再增加子选择器

  1. BAD
  2. treehead > treerow > treecell {…}
  3. GOOD
  4. .treecell-header {…}
  5. BAD
  6. treeitem[IsImapServer="true"] > treerow > .tree-folderpane-icon {…}
  7. GOOD
  8. .tree-folderpane-icon[IsImapServer="true"] {…

5.依靠继承

  1. BAD
  2. #bookmarkMenuItem > .menu-left { list-style-image: url(blah) }
  3. GOOD
  4. #bookmarkMenuItem { list-style-image: url(blah) }

  最后,我们来做个总结,网站编写CSS时,应该优先考虑使用class选择器,避免使用通配符选择器(*)和属性选择器(a[rel="external"]),后代选择器与标签选择器结合使用也应避免。使用id选择器的性能最好,但是编写时要注意其唯一性,谨慎使用。CSS3选择器(例如::nth-child(n)第n个孩子)在帮助我们锁定我们想要的元素的同时保持标记的干净和语义化,但事实是,这些花哨的选择器让更多的浏览器资源被密集使用。引用David Hyatt关于CSS3选择器的论述:如果你关心页面性能的话,他们真不该被使用!

先说selector效率优化策略三条:

1. 最右边的自选器精准化,以减少海选数。

 “.aaa”优化成“input.aaa”-----海选*.aaa变成了海选input.aaa

 “.ctn .aaa”优化成“.ctn input.aaa”-----海选*.aaa变成了海选input.aaa

2. 带上容器id,以减少海选数。

 “div.aaa”优化成“#container div.aaa”-----海选div.aaa变成了海选#container input.aaa

3. 除带id祖先或带id旁系亲属外,关系尽量简单,以减少血缘鉴定难度。

 “body input.aaa”优化成“input.aaa”-----减少了一层没必要的关系。

写页面的同学在很多情况下可以忽略性能(选择器已经做了很多优化),但是一些基本的优化策略,还是应该放在心里的。那我们再来看下selector性能比赛吧。

以司徒同学的新锐选择器作的速度的比赛试例为例(http://www.cnblogs.com/rubylouvre/archive/2010/01/27/1657684.html),来看下这个比赛到底有少可以再考虑的地方。

selector简评
body :empty多一层无效血缘鉴定。----也有部分选择器对body作了特别优化,会把它唯一性祖先看。
自选器没带tagName,考虑能否精准化
div:not(.example):not使用情况较少,速度比赛没有对使用概率进行加权,John Resig曾经写过一篇文章,统计过常用selector的使用概率的问题。
p:contains(selectors):contains使用情况较少
div p a三层血缘鉴定,血缘鉴定成本有点过高。参见“优化策略3”
div, p, a有“,”关系符,sizzle默认进行排序与除重。所以很慢。----默认排序有利有弊,很多selector放弃了这一策略。
div p血缘鉴定耗时
body div多一层无效血缘鉴定。----也有部分选择器对body作了特别优化,会把它唯一性祖先看。
.note自选器没带tagName,考虑能否精准化
ul.toc li.tocline2 
ul.toc > li.tocline2 
tr .pattern自选器没带tagName,考虑能否精准化
p 
div 
div ~ p使用概率低
div > div使用概率底
div ~ div使用概率低
div > p使用概率低
body部分选择器对body有优化
div + p使用概率低
div[class^=exa][class$=mple] 
div.example使用概率高。并且是推荐用法。可以考虑加权因素
ul .tocline2自选器没带tagName,考虑能否精准化
div.example, div.note有“,”关系符,sizzle默认进行排序与除重。所以很慢。----默认排序有利有弊,很多selector放弃了这一策略。
#title使用概率高。并且是推荐用法。可以考虑加权因素
h1#title 
div #title 
h1#title + div > p后半段使用概率低
a[href][lang][class] 
div[class] 
div[class=example] 
div[class^=exa] 
div[class$=mple] 
div[class*=e] 
div[class|=dialog] 
div[class!=made_up] 
div[class~=example] 
p:nth-child(even) 
p:nth-child(2n) 
p:nth-child(odd) 
p:nth-child(2n+1) 
p:nth-child(n) 
p:only-child 
p:last-child 
p:first-child 
div :only-child使用概率低。
多一层血缘鉴定。
自选器没带tagName,考虑能否精准化
div :only-of-type使用概率低。
多一层血缘鉴定。
自选器没带tagName,考虑能否精准化
th:first-of-type使用概率低。
th:last-of-type使用概率低。
td:nth-of-type(even)使用概率低。
td:nth-last-of-type(odd)使用概率低。
td:odd 
p:even 

综上,分析一下主要的几个问题:

速度比赛里,大部分的时间,都花在使用概率低、或是可以考虑改进的selector写法上。

所以,比赛的总分,参考意义不是很大。细看每项,可能收获更多。

唯一特别的“,”关系符。sizzle好像没有认真面对这个问题----也许的确现实中没多少人这么用。

目测:在有“,”选择符时,sizzle没有优化ie下的排序,不然,600多个元素,要花200ms,有点夸张。

在IE下,用以下这种方法sortBy(resultArr,function(el){return el.sourceIndex+100000000;}),耗时应该会减小到20ms以内吧----想当然的认为,没测过。
https://github.com/wedteam/qwrap/blob/master/resource/js/core/dev/array.h.js

免责声明:文章转载自《css选择符的渲染效率》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇QOS限速spark性能测试理论-Benchmark(转)下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

IE的有条件注释详解(附实例代码)

转自:http://www.cnblogs.com/JustinYoung/archive/2009/03/02/ie-jiaojianzhushi.html IE的有条件注释是一种专有的(因此是非标准的)、对常规(X)HTML注释的Miscrosoft扩展。顾名思义,有条件注释使你能够根据条件(比如浏览器版本)显示代码块(不一定就是css,也可以是htm...

vue-构建app项目

以下记录vue-cli 3构建app项目的步骤。 一、初始化配置,并运行启动app 1、安装nodeJS,git ,配置环境,Vue CLI 3.x 需要 Node.js 8.9 或更高版本 (推荐 8.11.0+)。 2、安装vue-cli ,命令:npm install -g @vue/cli 3、创建项目:vue create mapp-demo 推...

SwiftUI:视图修饰

前言 SwiftUI的视图修饰包含了 Controls修饰  控件修饰 Effects修饰   效果修饰 Layout修饰   布局修饰 Text修饰    文本修饰 Image修饰   图像修饰 List修饰    列表修饰 Navigation Bar  导航修饰 Styles修饰    风格修饰 Accessibility   访问修饰 Event...

vue组件如何引入外部.js/.css/.scss文件

可在相应的单vue组件引入相应文件。 1、引入外部.js文件。   2、引入外部.css文件。     使用@import引入外部css,作用域是全局的,也可在相应的单vue组件引入,import并不是引入代码到<style></style>里面,而是发起新的请求获得样式资源,并且没有加scoped。     注:如果有样式时,应该...

css中所有的选择器(包括比较少见的选择器)

jQuery、CSS常用选择器 符号 描述 示例 说明 紧接无符号 相当于”并且“关系 input.k-textbox{...} 选出input并且包含k-textbox类的元素 , (逗号) 选择器分格符, 选择多种元素 h1, h2{...} 选出h1 和 h2 的所有元素 . (圆点) 类选择器 .k-textbox{...} 选出...

CSS之使用display:inline-block来布局及浮动和inline-block的区别作用

https://www.cnblogs.com/Ry-yuan/p/6848197.html css之display:inline-block布局 1.解释一下display的几个常用的属性值,inline , block, inline-block inline(行内元素): 使元素变成行内元素,拥有行内元素的特性,即可以与其他行内元素共享一行,不会...