渲染的流程基本如下(黄色的四个步骤):
1.计算CSS样式
2.构建Render Tree
3.Layout 定位坐标和大小,是否换行,各种position,overflow, z-index属性 ……
compute style:计算样式
construct frames :构建框架
layout:页面布局
paint:绘画
注意:上图流程中有很多连接线,这表示了Javascript动态修改了DOM属性或是CSS属会导致重新Layout,有些改变不会,就是那些指到天上的箭头,比如,修改后的CSS rule没有被匹配到,等。
这里重要要说两个概念,一个是Reflow(回流),另一个是Repaint(重绘)。这两个不是一回事。
- Repaint——屏幕的一部分要重画,比如某个CSS的背景色变了。但是元素的几何尺寸没有变。
- Reflow——意味着元件的几何尺寸变了,我们需要重新验证并计算Render Tree。是Render Tree的一部分或全部发生了变化。这就是Reflow,或是Layout。(HTML使用的是flow based layout,也就是流式布局,所以,如果某元件的几何尺寸发生了变化,需要重新布局,也就叫reflow)reflow 会从<html>这个root frame开始递归往下,依次计算所有的结点几何尺寸和位置,在reflow过程中,可能会增加一些frame,比如一个文本字符串必需被包装起来。
Reflow的成本比Repaint的成本高得多的多。DOM Tree里的每个结点都会有reflow方法,一个结点的reflow很有可能导致子结点,甚至父点以及同级结点的reflow。在一些高性能的电脑上也许还没什么,但是如果reflow发生在手机上,那么这个过程是非常痛苦和耗电的。
所以,下面这些动作有很大可能会是成本比较高的。
- 当你增加、删除、修改DOM结点时,会导致Reflow或Repaint
- 当你移动DOM的位置,或是搞个动画的时候。
- 当你修改CSS样式的时候。
- 当你Resize窗口的时候(移动端没有这个问题),或是滚动的时候。
- 当你修改网页的默认字体时。
注:display:none会触发reflow,而visibility:hidden只会触发repaint,因为没有发现位置变化。
基本上来说,reflow有如下的几个原因:
- Initial。网页初始化的时候。
- Incremental。一些Javascript在操作DOM Tree时。
- Resize。其些元件的尺寸变了。
- StyleChange。如果CSS的属性发生变化了。
- Dirty。几个Incremental的reflow发生在同一个frame的子树上。
应该减少
reflow/repaint
1.不要一条一条地修改DOM的样式。与其这样,还不如预先定义好css的class,然后修改DOM的className。
// bad
var
left = 10,
top = 10;
el.style.left = left +
"px"
;
el.style.top = top +
"px"
;
// Good
el.className +=
" theclassname"
;
// Good
el.style.cssText +=
"; left: "
+ left +
"px; top: "
+ top +
"px;"
;
- 使用documentFragment 对象在内存里操作DOM
- 先把DOM给display:none(有一次reflow),然后你想怎么改就怎么改。比如修改100次,然后再把他显示出来。
- clone一个DOM结点到内存里,然后想怎么改就怎么改,改完后,和在线的那个的交换一下。