浏览器重绘和重排
本文地址: http://www.cnblogs.com/blackmanba/p/browser-repaint-reflow.html或者http://forkme.info/browser-repaint-reflow/, 转载请注明源地址。
概述
重排, 顾名思义就是重新排版的意思; 重绘, 就是浏览器重新绘制。理解重排和重绘的含义十分重要, 因为在评审页面交互效果的时候, 重绘和重排是必须考虑的因素。并不是说交互效果实现了就可以了, 必须同时考虑到这样做会引发什么性能问题。也就是说, 浏览器在进行重绘和重排的时候是要付出高昂的性能代价的。
浏览器执行流程
浏览器每次从服务器下载完页面后就会对页面进行渲染(Render), 这里面就包含了重绘以及重排。每种浏览器虽然工作原理略有差别, 但也遵循以下流程: 浏览器引擎会解析HTML文档来构建DOM树。树的每个节点都是标签, 有大小边距等等的属性, 这是因为每个HTML元素都遵循盒子模型(隐藏元素不包括在文档树中, 浏览器不会将其渲染)。渲染树构建完毕后, 浏览器就能够确定每个元素的位置并将元素放到正确的位置上, 再根据树节点的样式属性绘制出页面元素。由于浏览器的流布局的方式, 对渲染树的计算通常只需要遍历一遍即可。但table及其内部元素除外, 可能需要执行多次计算才能确定好在渲染树中的属性, 这个过程通常要耗费3倍以上的时间。这也是我们要避免使用table标签的其中一个原因。
触发条件
重绘
重绘是元素改变外观时所触发的浏览器行为, 不包括修改元素的几何属性。例如改变visibility, outline, background等属性。浏览器会根据新的属性重新绘制, 使元素呈现新的外观。重绘不一定会带来新的布局, 并不一定伴随着重排。
重排
-
DOM元素的几何属性变化
当DOM的几何属性变化时, 渲染树中的相关节点就会失效, 浏览器会重新构建渲染树中失效的节点。而且, 当前元素的重排也许会带来相关元素的重排。例如, 容器节点渲染树改变时, 会触发子节点的重新计算, 也会触发后续兄弟节点的重排, 祖先节点需要重新计算大小, 最后, 每个元素可能都会进行重绘。可见, 重排一定会引起重绘, 并且因为重排的元素很多, 导致重排从性能上来说比重绘更差。一个元素的重排通常都会带来一系列反应, 甚至触发整个文档的重绘和重排, 性能代价是高昂的。 -
DOM树的结构变化
当DOM树的结构变化时, 例如节点的增加, 减少, 删除, 也会触发重排。浏览器引擎渲染DOM树类似前序遍历, 也就是说当前元素不会影响前面已经遍历过的元素。所以, 如果在body前面插入一个元素, 就会导致整个文档的重新渲染, 而在其后插入一个元素, 就不会影响到前面元素的布局。 -
获取某些属性
浏览器会对重排进行优化, 可能会等到有足够数量的变化发生, 或者等到一定时间, 或者等一个线程结束, 再一起处理。这样就只会发生一次重排。但如果渲染树直接发生变化, 当获取一些属性时, 浏览器为了取得正确的值也会触发重排。这样就使得浏览器的优化失效。这些属性包括: offsetTop、offsetLeft、 offsetWidth、offsetHeight、scrollTop、scrollLeft、scrollWidth、scrollHeight、clientTop、clientLeft、clientWidth、clientHeight、getComputedStyle() (currentStyle in IE)。所以,在多次使用这些值时应进行缓存。 -
其他: 比如改变元素的某些样式, 调整浏览器窗口等等也会触发重排。
最佳实践
- 避免在document上直接进行频繁的DOM操作, 如果确实需要可以采用off-document方式进行;
- 集中修改样式
- 缓存layout属性值
- 设置position为absolute或者fixed
- 权衡动画速度和CPU性能
- 不要使用table布局
- 不要在css中写expression
判别方式
如何判断页面重绘和重排的性能, 在google chrome 上可以使用Timeline面板, 具体使用请查看这篇文章