最近研究css性能优化,明白了浏览器读取css选择器的顺序是从右到左。但是为什么呢?这就涉及到了浏览器渲染页面的顺序,而关于浏览器的渲染,我们首页要了解浏览器的架构
浏览器构架图
如上图所示,浏览器主要由 用户界面(User Interface), 浏览器引擎(Browser engine), 渲染引擎(Rendering engine), 网络模块(Networking),js解析器(Javascript Interpreter),用户界面后台(UI Backend)和数据持久层(Data persistence) 等几部分组成。其中各模块除了Browser engine是用来协调Render engine和UI层(也许还有别的层)的,UI Backend是用来绘制页面上的各个组件的,其它模块的用途都见名知义,所以也就不多说了。
OK,进入正题。从用户在地址栏中输入地址并按下回车,到他看到整个页面的过程大致如下:
1.用户在浏览器地址栏输入地址,按下回车;
2. 浏览器向服务器发送请求,服务器响应请求并返回数据;(这其中的DNS解析,路由解析,服务器mvc请求分发,连接数据库等一系列操作略过)
3.浏览器接收服务器传回的html代码,通过词法解析和语法解析生成dom树,生成dom树期间,解析到link标签则去下载相应的css文件,待所有外部css文件下载完成后,结合页面中的style标签和标签行内style样式,生成render树. render树包含了每个dom节点的样式信息(位置,大小,字体,背景等)。
4.结合dom树和render树绘制页面,如下图所示:
2. 浏览器向服务器发送请求,服务器响应请求并返回数据;(这其中的DNS解析,路由解析,服务器mvc请求分发,连接数据库等一系列操作略过)
3.浏览器接收服务器传回的html代码,通过词法解析和语法解析生成dom树,生成dom树期间,解析到link标签则去下载相应的css文件,待所有外部css文件下载完成后,结合页面中的style标签和标签行内style样式,生成render树. render树包含了每个dom节点的样式信息(位置,大小,字体,背景等)。
4.结合dom树和render树绘制页面,如下图所示:
渲染引擎基本工作流程
我们要研究的问题发生在步骤3,构建render树的过程中。
构建render树的过程是遍历dom树, 每次拿出一个dom节点,然后遍历所有的样式规则查找与当前节点匹配的规则,最后将所有匹配的规则中定义的样式写入一个render对象中,再将该render对象挂到render树上(这个render对象和 dom节点会以某种方式建立联接,知道彼此的存在)。
也就是说,每次只有一个dom节点,且该节点标签名称,拥有的class和id等我是已知的,例如<span class="abc" id="demo">,但却可能有成千上万条css规则(这个数量并不夸张),我们需要从这多的规则中找中符合当前的节点的1条或几条规则(这个数量绝不会很多)。
由于每条规则都可能有多层嵌套,例如 #container p.content .title a {...},如果采用从左到右的方式读取css规则,那么大多数规则读到最后会发现是不匹配的,这样会做很多无用功。
而如果采取从右到左的方式,那么只要发现最右边的key selector不匹配,整条规则就都不必再看下去了。例如当前节点是<span class="abc" id="demo">, 那么只有最右端选择器是span或.abc或#demo的css 规则有可能匹配,其它的就可以直接被舍弃了。
根据2009年Google和Firefox的测试,right-to-left方式可以避免70%左右的无效匹配,因此目前主流浏览器都采用这种方式读取css selector(css规则).
参考资料:
http://stackoverflow.com/questions/5797014/why-do-browsers-match-css-selectors-from-right-to-left
http://taligarsiel.com/Projects/howbrowserswork1.htm
注: 文中图片均引自http://taligarsiel.com/Projects/howbrowserswork1.htm