如何提升 CSS 选择器性能

TOC
  1. 1. CSS样式解析是从右到左的
    1. 1.1. 为什么
    2. 1.2. 解答
  2. 2. 优化方法
  3. 3. 其他可参考资料

CSS样式解析是从右到左的

为什么

我们在写css样式的时候,几乎都是根据[从左到右]的思维来写的,于是我们就理所当然的认为,浏览器解析CSS的时候, 也是按照从左到右的顺序来的.然而并不是,至于为什么,那就得先了解一下浏览器的渲染原理.这里我在重新梳理一遍,以加深自己对浏览器渲染的认识.

解答

这部分内容引自:

渲染树

HTML 经过解析生成 DOM Tree(这个我们比较熟悉);而在 CSS 解析完毕后,需要将解析的结果与 DOM Tree 的内容一起进行分析建立一棵 Render Tree,最终用来进行绘图。Render Tree 中的元素(WebKit 中称为「renderers」,Firefox 下为「frames」)与 DOM 元素相对应,但非一一对应:一个 DOM 元素可能会对应多个 renderer,如文本折行后,不同的「行」会成为 render tree 种不同的 renderer。也有的 DOM 元素被 Render Tree 完全无视,比如 display:none 的元素。

在建立 Render Tree 时(WebKit 中的「Attachment」过程),浏览器就要为每个 DOM Tree 中的元素根据 CSS 的解析结果(Style Rules)来确定生成怎样的 renderer。对于每个 DOM 元素,必须在所有 Style Rules 中找到符合的 selector 并将对应的规则进行合并。选择器的「解析」实际是在这里执行的,在遍历 DOM Tree 时,从 Style Rules 中去寻找对应的 selector。

因为所有样式规则可能数量很大,而且绝大多数不会匹配到当前的 DOM 元素(因为数量很大所以一般会建立规则索引树),所以有一个快速的方法来判断「这个 selector 不匹配当前元素」就是极其重要的。

如果正向解析,例如「div div p em」,我们首先就要检查当前元素到 html 的整条路径,找到最上层的 div,再往下找,如果遇到不匹配就必须回到最上层那个 div,往下再去匹配选择器中的第一个 div,回溯若干次才能确定匹配与否,效率很低。

逆向匹配则不同,如果当前的 DOM 元素是 div,而不是 selector 最后的 em,那只要一步就能排除。只有在匹配时,才会不断向上找父节点进行验证。

但因为匹配的情况远远低于不匹配的情况,所以逆向匹配带来的优势是巨大的。同时我们也能够看出,在选择器结尾加上「*」就大大降低了这种优势,这也就是很多优化原则提到的尽量避免在选择器末尾添加通配符的原因。

优化方法

既然CSS是从右到左匹配,那么就要尽可能提高解析效率.
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)

根据以上「选择器匹配」与「选择器执行效率」原则,我们可以通过避免不恰当的使用,提升 CSS 选择器性能:

  • 避免使用通配符
  • 避免限制限制 id 选择器
  • 避免使用标签限制 class 选择器
  • 避免使用多层标签选择器,可以用class代替
  • 避免使用子选择器
  • 使用继承

其他可参考资料

访客评论