接上一篇浏览器渲染的那些事(一)继续说。
构建呈现树 Render Tree/Frame Tree
渲染的流程:
在这部分我们来讲一下构建Render Tree的过程。
呈现树主要是负责布局并将自身及其子元素绘制出来。
Webkits RenderObject类是所有呈现器的基类。定义如下:
class RenderObject{
virtual void layout();
virtual void paint(PaintInfo);
virtual void rect repeatRect();
Node* node; //DOM node
RenderStyle* style; //the computed style
RenderLayer* containgLayer; //the
}
每个呈现器都代表了一个矩形区域,一般对应于相关节点的css框,包含宽度、高度、位置等几何信息。
不过对于一些具有复杂结构的元素,就对应了几个可见对象,这就不是一个矩形能表现出来的了。例如select元素【对应一个显示区域,一个下拉列表以及一个按钮】。
不规范的html也会产生多个渲染对象。【css规范中,一个行内元素只能仅包含行内元素或仅包含块状元素,在存在混合内容时,将会创建匿名的块状渲染对象包裹住行内元素。】
呈现树与DOM树关系
呈现树与DOM元素相对应,但不是一一对应。
非可视化的DOM元素就不会插入呈现树,例如head元素。
display为"none"的元素也不会显示在呈现树中【visibility:hidden的元素仍会显示】
除此之外,一些渲染对象和其对应的DOM节点不是在树上的位置也有所不同。
例如浮动元素和绝对定位元素在文本流之外,那么呈现树会标识出真实的结构,并用一个占位结构标识出它们原先的位置。
webkit代码中说明了如何根据display属性决定某个节点创建什么对象的渲染对象。
RenderObject* RenderObject::createObject(Node* node, RenderStyle* style)
{
Document* doc = node->document();
RenderArena* arena = doc->renderArena();
...
RederObject* o = 0;
switch(style->display()){
case NONE:
break;
case INLINE:
o = new (arena) RenderInline(node);
break;
case BLOCK:
o = new (arena) RenderBlock(node);
break;
...
}
return o;
}
样式处理
构建呈现树的时候,需要计算每一个呈现对象的可视化属性,也就是计算每个元素的样式属性来完成。这部分比较繁琐。有的部分是我自己翻译理解的【如果有误请指出,谢谢】
样式计算的复杂性
样式计算是个复杂的工程,首先存储了无数的样式属性,可能会造成内存问题;如果没有进行优化,那么为每个元素查找匹配的规则都需要遍历一遍规则列表,而且选择器的结构可能会造成走错匹配路径。除此之外,应用规则也有复杂的层叠规则。【!important需要创建一个额外的规则对象 CSSImportantRule】webkit内核浏览器的处理方案
webkit有样式对象,直接把这个style对象存在对应的DOM结点上。因此会对匹配的声明遍历4次,首先应用非重要高优先级的属性,其次是高优先级重要规则,然后是普通优先级非重要规则,最后是普通优先级重要规则。多次出现的属性会根据正确的层叠顺序进行解析,最后出现的最终生效。firefox的处理方案
firefox采用了规则树和样式上下文树来简化样式计算。
规则树包含了所有已经知道规则匹配的路径。
在计算某个特定元素的样式上下文时,先计算规则树中的对应路径,或使用现有路径,从路径中最高优先级的底层节点开始,向上遍历规则树,直到在新的样式上下文中结构填充完成。也就是css优先级。(在之前写的css选择器中有讲到。)【规则树只有当某个节点样式需要计算时,才会添加新的计算路径。不会在开始时候就为所有节点进行计算】。
如果没有满足这个元素的样式,对于inherit的属性就会使用initial value【例如'font-size','color'】,对于reset类型的属性就使用默认值【例如'border','background'】
-
css rule processor处理方式
css rule processor会将所有规则按照级联顺序排序,然后放入RuleHash。哈希表的选择器各不相同,包括ID,class,标记名称等。【例如,如果选择器是ID,就把规则放入ID的哈希表中】还有一种通用哈希表,适合不属于上述类别的规则。匹配样式时,就在RuleHash's table查找,然后合并已经保存的样式列表,然后SelectorMatchesTree 会去找真正匹配的那个选择器。
【查找id和class的速度比属性选择器快得多!】
对于伪元素,是保存在一个元素hash内,所以查找伪元素只需要查找一个哈希表。
有点难理解,看个栗子【来自MDN】。
//HTML代码
<doc><title>A few quotes</title> <para class="emph"> Franklin said that <quote>"A penny saved is a penny earned."</quote> </para> <para> FDR said <quote>"We have nothing tofear but <span class="emph">fear itself.</span>" </quote> </para>
</doc>
//css样式如下
/ rule 1 / doc { display: block; text-indent: 1em; }/* rule 2 */ title { display: block; font-size: 3em; } /* rule 3 */ para { display: block; } /* rule 4 */ [class="emph"] { font-style: italic; }
Rule tree规则树如下:
Style context tree样式上下文树如下:
4.匹配规则顺序
前面有说到样式对象的属性,如果定义有多个,那么就需要通过层叠顺序来决定最终的显示效果。
层叠顺序优先级由高到低:
用户重要声明>作者重要声明>作者普通声明>用户普通声明>浏览器声明选择器的优先级呢,可以看我之前写的css选择器总结
规则排序
匹配段规则会根据级联顺序进行排序。webkit对于较小列表使用冒泡排序,对于较大的列表使用归并排序。
参考文献:
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。