样式计算会带来一些困难:
1 样式数据是庞大的结构,具有非常多的样式属性,这可能会带来内存问题。
2 为每个元素找到匹配的规则的过程如果不加优化,可能导致性能问题。为每个元素遍历整个列表以找到匹配是沉重的负担。选择器可能有复杂的结构,也许会导致匹配过程起始于看起来很有希望的路径,结果却证实扑空,只得尝试另外一条路径。
例如—这个复合选择器:
div div div div{ ... }
意思是应用到一个“<div>”的规则外面有三层div。假设你想检查一下规则是否应用到了给定的“<div>”元素,你需要选择沿树而上的某条路径来检查。你可能需要往上遍历节点树,发现只有两个div,规则应用不上。然后你就需要尝试树上的另一条路径。
3 应用规则可能牵涉到定义规则层次的十分复杂的级联规则。
我们看看浏览器如何面对这些问题:
共享样式数据:
Webkit节点引用样式对象(RenderStyle),这些对象在某些情况下可以被节点共享。这些节点必须是兄弟或远亲节点,并且:
1 这些元素们必须处于同一鼠标状态(举例来说,不能一个元素处在鼠标的hover状态而另一个不是)。
2 元素们都不能有id。
3 tag名必须能匹配上。
4 class属性必须能匹配上。
5 映射属性集必须相同。
6 链接状态必须匹配上。
7 焦点状态必须匹配上。
8 绝不能有元素被属性选择器影响,这里的影响定义为有任何选择器符合在这个选择器内的任何位置使用一个属性选择器。
9 在元素上必须没有内联样式属性。
10 绝对不能有任何兄弟选择器被使用。WebCore简单的提供一个全局开关,在样式共享可用的情况下,当遇到任何兄弟选择器时在整个文档内禁用样式共享。
Firefox规则树
Firefox有另外两棵树,用来简化样计算—规则树和样式内容树。Webkit也有样式对象,但并不象样式内容树一样以树的形式储存,只把DOM节点指向它的相关样式。
图13:Firefox的样式内容树
样式内容包含结束值。通过以正确的顺序应用所有匹配规则,执行从逻辑值到具体值的转换操作。例如—如果逻辑值是屏幕百分比,它会被计算和转换成绝对单位。规则树的想法实在聪明。它允许在节点节共享这些值,避免重复计算它们。这也能节省空间。
所有匹配的规则保存成一棵树。路径中的底部节点有更高的优先级。这棵树包含所有匹配已发现的规则的路径。保存这些规则的过程是逐渐发生的。这棵树并不是在每个节点的开始计算,但任何时候只要一个节点的样式需要计算时,计算出的路径就会被添加到这棵树上。
这种想法是把树上的路径看成词典里的单词。我们假设已经计算出了规则树:
假设我们需要匹配内容树里的另外一个元素,找到的匹配规则(以正确的顺序)是B-E-I。我们在树里面已经有这个路径了,因为我们已经计算过路径A-B-E-I-L。我们现在就省事了。
让我们看一下这棵树工作时怎么保存的。
拆分成结构
样式内容被拆分成结构。这些结构包含某种类别的样式信息,象border或color等。所有结构中的属性或者是继承的或者非继承的。继承的属性是那些除非元素自己定义,否则从它的父元素继承的属性。非继承的属性(称为“reset”属性)如果没有定义的话使用缺省值。
这棵树通过把整个结构(包含计算结束值)缓存在树里为我们提供方便。想法是如果底部节点没有提供某个结构的定义,那就使用一个在更上层结点的缓存过的结构。
(待续)
原创文章,作者:苏葳,如需转载,请注明出处:https://www.swmemo.com/2068.html