Repaint和Reflow
是什么?
浏览器解析大概的工作流程:
- 解析HTML以构建DOM树:渲染引擎开始解析HTML文档,转换树中的html标签或js生成的标签到DOM节点,它被称为 – 内容树。
- 构建渲染树:解析CSS(包括外部CSS文件和样式元素以及js生成的样式),根据CSS选择器计算出节点的样式,创建另一个树 —- 渲染树。
- 布局渲染树: 从根节点递归调用,计算每一个元素的大小、位置等,给每个节点所应该出现在屏幕上的精确坐标。
- 绘制渲染树: 遍历渲染树,每个节点将使用UI后端层来绘制。
可以看到Repain 和 Reflow 分别出现在了第三和第四步。定义为如下:1
2对于DOM结构中的各个元素都有自己的盒子(模型),这些都需要浏览器根据各种样式(浏览器的、开发人员定义的等)来计算并根据计算结果将元素放到它该出现的位置,这个过程称之为reflow;当各种盒子的位置、大小以及其他属性,例如颜色、字体大小等都确定下来后,浏览器于是便把这些元素都按照各自的特性绘制了一遍,于是页面的内容出现了,这个过程称之为repaint。
可见这两个东东对浏览器渲染页面是很重要的啊,都是会影响性能的,因此我们需要了解一些常见的会引起repaint和reflow的一些操作,并且应该尽量减少以提高渲染速度。
引起原因
Reflow 的成本比 Repaint 的成本高得多的多。DOM Tree 里的每个结点都会有 reflow 方法,一个结点的 reflow 很有可能导致子结点,甚至父点以及同级结点的 reflow。在一些高性能的电脑上也许还没什么,但是如果 reflow 发生在手机上,那么这个过程是非常痛苦和耗电的。
Repaint
repaint一般在改变 DOM 元素的视觉效果时触发,即不涉及任何排版布局的问题时触发,它主要针对的是某一dom元素的重新绘制。当一个元素的外观的可见性visibility发生改变的时候,重绘(repaint)也随之发生,但是不影响布局。类似的例子包括:outline, visibility, or background color
。一般触发repaint的常见场景有以下几个:
- color的修改,如color=#ddd;
- text-align的修改,如 text-align:center;
- a:hover也会造成重绘;
- :hover引起的颜色等不导致页面回流的style变动;
等等。
Reflow
reflow回流是在某一个 DOM 元素的位置发生改变后触发,而且它会重新计算所有dom元素的位置和在页面中的占有的面积,所以一般来说,reflow的影响比repaint要大得多,它将会影响它所有的children、ancestors及siblings。所以影响是针对整个页面的,整个页面都需要重新渲染。一般触发reflow的常见场景有以下几个:
- 使用JS改变DOM元素时会触发reflow,即添加(appendChild)、删除(removeChild)DOM元素或者改变DOM元素的可见性(display:none)
- CSS中width/height/border/margin/padding的修改,如width=778px;
- CSS3 动画(animation)和过渡(transition),动画的每一frame都会触发reflow;
- 读取元素的某些属性(offsetLeft/Top、offsetHeight/Width、scrollTop/Left/Width/Height、clientTop/Left/Width/Height、getComputedStyle()、currentStyle(in IE))时会触发reflow,因为这些属性需要依赖一些元素去计算;
- :hover等伪类引起的元素表现改动造成页面回流;
- scroll页面,这个不可避免;
- 字体大小改变或更换字体 (font);
- resize页面,桌面版本的进行浏览器大小的缩放;等等。
优化
如果想设定元素的样式,通过改变元素的 class 名 (尽可能在 DOM 树的最末端)(Change classes on the element you wish - to style (as low in the dom tree as possible))
避免设置多项内联样式(Avoid setting multiple inline styles)
应用元素的动画,使用 position 属性的 fixed 值或 absolute 值(Apply animations to elements that are position fixed or absolute)
权衡平滑和速度(Trade smoothness for speed)
避免使用table布局(Avoid tables for layout)
避免使用CSS的JavaScript表达式 (仅 IE 浏览器)(Avoid JavaScript expressions in the CSS (IE only))
尽可能在DOM树的最末端改变class
回流可以自上而下,或自下而上的回流的信息传递给周围的节点。回流是不可避免的,但可以减少其影响。尽可能在DOM树的里面改变class,可以限制了回流的范围,使其影响尽可能少的节点。例如,你应该避免通过改变对包装元素类去影响子节点的显示。面向对象的CSS始终尝试获得它们影响的类对象(DOM节点或节点),但在这种情况下,它已尽可能的减少了回流的影响,增加性能优势。
尽量采取批量更新元素样式的方式,比如可以将需要修改的样式集合放在一个class里,将这个class附给元素。
避免设置多层内联样式
我们都知道与DOM交互很慢。我们尝试在一种无形的DOM树片段组进行更改,然后整个改变应用到DOM上时仅导致了一个回流。同样,通过style属性设置样式导致回流。避免设置多级内联样式,因为每个都会造成回流,样式应该合并在一个外部类,这样当该元素的class属性可被操控时仅会产生一个reflow。尽可能的减少DOM元素相互影响
减少DOM的层级,减少DOM的数量,DOM数量越少越好,慎改class!!去改子元素少的DOM的class动画效果应用到position属性为absolute或fixed的元素上
动画效果应用到position属性为absolute或fixed的元素上,它们不影响其他元素的布局,所它他们只会导致重新绘制,而不是一个完整回流。这样消耗会更低。删除复杂动画,尽量给动画元素设置position:fixed/absolute,使动画元素从DOM流中独立出来,从而减少对其他元素的影响。牺牲平滑度换取速度
Opera还建议我们牺牲平滑度换取速度,其意思是指可能想每次1像素移动一个动画,但是如果此动画及随后的回流使用了100%的CPU,动画就会看上去是跳动的,因为浏览器正在与更新回流做斗争。动画元素每次移动3像素可能在非常快的机器上看起来平滑度低了,但它不会导致CPU在较慢的机器和移动设备中抖动。尽量牺牲平滑度去满足性能,因为动画精度太强,会造成更多次的repaint/reflow避免使用table布局
table的每个元素的大小以及内容的改动,都会导致整个table进行重新计算,造成大幅度的repaint或者reflow。改用div则可以进行针对性的repaint和避免不必要的reflow.
避免使用table布局。可能您需要其它些避免使用table的理由,在布局完全建立之前,table经常需要多个关口,因为table是个和罕见的可以影响在它们之前已经进入的DOM元素的显示的元素。想象一下,因为表格最后一个单元格的内容过宽而导致纵列大小完全改变。然而有另外一个原因为什么表格布局是很糟糕的主意,根据Mozilla,即使一些小的变化将导致表格(table)中的所有其他节点回流。
Jenny Donnelly, YUI 数据表格 widget的所有者,建议使用数据表格的固定布局以便更有效的布局算法,任何表格-布局的值除了”auto”将引发一个固定布局,根据CSS2.1规范,这将允许表格一行一行的呈递。Quirksmode显示,大部分的浏览器对表格布局属性支持良好。避免使用CSS的JavaScript表达式
这项规则较过时,但确实是个好的主意。主要的原因,这些表现是如此昂贵,是因为他们每次重新计算文档,或部分文档、回流。正如我们从所有的很多事情看到的:引发回流,它可以每秒产生成千上万次。当心!尽量简写CSS样式
可以把许多样式合在一起的就应该把他们合在一起避免重新重复使用。