CSS引擎渲染步骤和Firefox的优化内容
最近在firefox的开发者官网上看见了css引擎(Quantum CSS )优化,刚好提到了css渲染,作者图文并茂,还运用了一些比喻的方法帮助理解和记忆。因此,在这里引用下,并将里面的内容用自己理解转述如下。
笔者水平和精力有限,依靠个人理解加工,完全没有全文和逐字逐句地翻译,感兴趣的同学可以直接原文阅读。
一、渲染步骤
1. parse
将html文件解析成DOM树。这里DOM 树虽然知道节点之间的关系,但是不知道这些节点能渲染出来的样子;
2. style
计算每个元素应该的样式,比如每个元素的样式表里的内容属性都是什么,但是没有绘制;
3. layout
布局,计算每个元素应该的尺寸以及在屏幕上的位置;
4. paint
绘制不同层次的box,像老式的动画片,一张一张的层叠,但是却会保障一个层次的box不会绘制在不同层,这里准确的说应该是FPS
超小到每帧都完全不一样的老式动画片;
5. composite & render
合成和渲染,将各层合并到一块,就像从上拍照渲染在浏览器上,看到什么要就是什么样,层叠早已被确定。
二、css 引擎
在上述步骤中css引擎做了如下几件事:
- 遍历DOM
- 为每个Node找到对应的所有css属性
完成后,一个节点所有属性合成在一块会生成一个表格(对象):
为了做到上面两点,css引擎需要:
- 通过选择器为node匹配css rule —
selector matching
- 从祖先节点或者默认属性值中取得某属性的值 —
cascade
selector matching
选择器匹配css规则,找到所有和该节点相关的css属性规则,另外,再找到一些默认的css样式(user agent style sheets
)。之后,css引擎会将这些样式生成一张列表,从上至下找到优先级最高的样式。
以上方法,虽然能补充node的部分属性,但并不是所有属性都能被设置,或者说有默认属性,这些剩余的属性就需要cascade
。
cascade
cascade
是堆叠的意思,也就是CSS全称中C的简写,层叠式样式表。
cascade让编写在body等祖先元素上的css属性可以被子孙使用,除非子孙本身上的属性能够覆盖它,因此,css引擎会先看属性表上的属性值是否为空,如果是的就会在祖先上查找,如果祖先上都没有,那就使用上述默认的值。
三、css引擎的优化
上面提到每个node都要维护一个关于所有css属性(表格)对象,这需要很多内存。目前大部分浏览器的优化方法是将这个大对象切割成小的对象,这些对象能共享,当两个节点这些属性一致时,利用指针索引来重复使用。
firefox的css引擎优化
虽然有了将大的属性表切割成小的、共享的表,但是更大的问题是每当用户交互发生后,都需要重新进行上面的绘制,这些是无法避免的。针对这些,firefox的策略是:
-
Run it all in parallel
大家都知道现在的计算机基本都有多个cpu,新版设计的firefox希望让每个CPU并行(parallel)处理DOM结构中的一部分。但是问题是,DOM结构不可能刚好深度和数量是平均的,Quantum CSS的做法是将所有的任务生成队列,当其他CPU提前完成任务时,可以帮助任务过重的CPU。
-
Speed up restyles with the Rule Tree
通常CSS 引擎需要一个节点所有的css rules才能完成selector matching,但是对大多数节点来说,css的继承关系几乎是没有变化的,如果我们能记录第一次selector matching的结果(继承的关系)就不需要再次全部重新计算了,在关系树上发现不匹配后才添加新的规则。
-
Speed up initial render (and the cascade) with the style sharing cache
如果在每个节点计算之前查看之前是否有相同的元素,直接利用之前缓存的规则也能提高性能,Safari and Chrome也做到了这一点(不同浏览器对部分添加伪类的节点运用缓存策略不同),主要是以下几种方法:
- 两个节点是否有相同的id和class,相同则使用相同的rules
- 如果不是有自身selector的行内元素,如果它们里面的值相同,那也使用同一套rules
- 如果父元素使用的是同一套rules,那子元素继承的rules也使用一套