全面详解 CSS 原理( 二 )


2、在建立 Render Tree 时(WebKit 中的「Attachment」过程),浏览器就要为每个 DOM Tree 中的元素根据 CSS 的解析结果(Style Rules)来确定生成怎样的 renderer 。对于每个 DOM 元素,必须在所有 Style Rules 中找到符合的 selector 并将对应的规则进行合并 。选择器的「解析」实际是在这里执行的,在遍历 DOM Tree 时,从 Style Rules 中去寻找对应的 selector 。
3、因为所有样式规则可能数量很大,而且绝大多数不会匹配到当前的 DOM 元素(因为数量很大所以一般会建立规则索引树),所以有一个快速的方法来判断「这个 selector 不匹配当前元素」就是极其重要的 。
4、如果正向解析,例如「div div p em」,我们首先就要检查当前元素到 html 的整条路径,找到最上层的 div,再往下找,如果遇到不匹配就必须回到最上层那个 div,往下再去匹配选择器中的第一个 div,回溯若干次才能确定匹配与否,效率很低 。
对于上述描述,我们先有个大概的认知 。接下来我们来看这样一个例子,参考地址:
<div>   <div class="jartto">      <p>span> 111 span><p>      <p>span> 222 span><p>      <p><span> 333 <span><p>      <p><span class='yellow'> 444 <span><p>   <div><div>CSS 选择器:
div > div.jartto p span.yellow{   color:yellow;}对于上述例子,如果按从左到右的方式进行查找:
1、先找到所有 div 节点;2、在 div 节点内找到所有的子 div ,并且是 class = “jartto”;3、然后再依次匹配 p span.yellow 等情况;4、遇到不匹配的情况,就必须回溯到一开始搜索的 div 或者 p 节点,然后去搜索下个节点,重复这样的过程 。

这样的搜索过程对于一个只是匹配很少节点的选择器来说,效率是极低的,因为我们花费了大量的时间在回溯匹配不符合规则的节点 。
如果换个思路,我们一开始过滤出跟目标节点最符合的集合出来,再在这个集合进行搜索,大大降低了搜索空间 。来看看从右到左来解析选择器:
1、首先就查找到 的元素;2、紧接着我们判断这些节点中的前兄弟节点是否符合 P 这个规则,这样就又减少了集合的元素,只有符合当前的子规则才会匹配再上一条子规则 。
结果显而易见了,众所周知,在 DOM 树中一个元素可能有若干子元素,如果每一个都去判断一下显然性能太差 。而一个子元素只有一个父元素,所以找起来非常方便 。
试想一下,如果采用从左至右的方式读取 CSS 规则,那么大多数规则读到最后(最右)才会发现是不匹配的,这样会做费时耗能,最后有很多都是无用的;而如果采取从右向左的方式,那么只要发现最右边选择器不匹配,就可以直接舍弃了,避免了许多无效匹配 。
浏览器 CSS 匹配核心算法的规则是以从右向左方式匹配节点的 。这样做是为了减少无效匹配次数,从而匹配快、性能更优 。
四、CSS 语法解析过程CSS 样式表解析过程中讲解的很细致,这里我们只看 CSS 语法解释器,大致过程如下:
1、先创建 CSSStyleSheet 对象 。将 CSSStyleSheet 对象的指针存储到 CSSParser 对象中 。2、CSSParser 识别出一个 simple-selector ,形如 “div” 或者 “.class” 。创建一个 CSSParserSelector 对象 。3、CSSParser 识别出一个关系符和另一个 simple-selecotr ,那么修改之前创建的 simple-selecotr, 创建组合关系符 。4、循环第3步直至碰到逗号或者左大括号 。5、如果碰到逗号,那么取出 CSSParser 的 reuse vector,然后将堆栈尾部的 CSSParserSelector 对象弹出存入 Vecotr 中,最后跳转至第2步 。如果碰到左大括号,那么跳转至第6步 。6、识别属性名称,将属性名称的 hash 值压入解释器堆栈 。7、识别属性值,创建 CSSParserValue 对象,并将 CSSParserValue 对象存入解释器堆栈 。8、将属性名称和属性值弹出栈,创建 CSSProperty 对象 。并将 CSSProperty 对象存入 CSSParser 成员变量m_parsedProperties 中 。9、如果识别出属性名称,那么转至第6步 。如果识别右大括号,那么转至第10步 。10、将 reuse vector 从堆栈中弹出,并创建 CSSStyleRule 对象 。CSSStyleRule 对象的选择符就是 reuse vector, 样式值就是 CSSParser 的成员变量 m_parsedProperties。11、把 CSSStyleRule 添加到 CSSStyleSheet 中 。12、清空 CSSParser 内部缓存结果 。13、如果没有内容了,那么结束 。否则跳转至第2步 。


推荐阅读