前面的话
这篇文章从回流会重绘的角度谈谈如何优化浏览器性能
什么是回流与重绘?
回流:
在浏览器渲染过程中,生成渲染树之后,根据渲染树对可见DOM节点进行布局,还需要进行计算它们在设备视口(viewport)内的确切位置和大小,这个计算的阶段就是回流。
重绘:
根据渲染树以及布局阶段,知道了DOM节点的样式与位置大小信息,那么就可以将渲染树的每一个节点转换为屏幕上的实际像素,这个阶段就叫重绘。
何时发生回流与重绘?
回流阶段主要是计算节点的位置与大小,会重新布局。重绘是根据DOM节点的样式与布局阶段再去绘制每个节点。 那么当页面布局和节点的位置大小信息发生变化时,就会发生回流。
回流:
- 添加或删除DOM,或元素的位置、尺寸发生变化
- 内容发生变化,比如文本变化或图片被另外一个不同的尺寸图片所代替
- 页面一开始渲染的时候(这肯定避免不了)
- 浏览器的窗口尺寸变化(因为回流是根据视口的大小来计算元素的位置和大小的)
重绘:
- 改变某个元素的背景色、文字颜色、边框颜色等等不影响它周围或内部布局的属性时,屏幕的一部分要重画,但是元素的几何尺寸没有变。
[注意]: 回流必定会发生重绘,重绘不一定会引发回流。回流所需得成本比重绘高得多,改变父节点里得子节点很可能会导致父节点得一系列回流。
浏览器的优化机制
由于每次回流重绘都会造成额外的计算消耗,因此大多数浏览器都会通过队列化修改并批量执行来优化重排过程。浏览器会将修改操作放到队列里,直到过了一段时间或者达到一个阈值,才会清空队列。
但是,当你获取布局信息的操作时,会强制刷新队列。比如访问一下属性:
- offset家族: offsetTop、offsetLeft、offsetWidth、offsetHeight
- scroll家族:scrollTop、scrollLeft、scrollWidth、scrollHeight
- client家族:clientTop、clientLeft、clientWidth、clientHeight
- getComputedStyle()
- getBoundingClientRect
使用以上属性或方法都要返回最新的布局信息,浏览器不得不清空队列,触发回流重绘。所以在修改样式时, 最好避免出现上面的属性方法。如果要是用,最好先把值缓存起来。
减少重绘与回流
css方面:
- 使用transform代替 top, left ,margin-top, margin-left… 这些位移属性
当一段时间改变元素的位置时,使用top会有很明显回流时间,使用transform页面的回流会直接没有,如果频繁改变位置两者的效果十分明显。transform: translateY(200px)代替 position: relative; top: 200px;
- 使用visibility代替display: none ,前者只会引起重绘,后者引发回流。有必要时使用opacity(透明度)来代替display: none
- 避免使用table布局: 可能很小的改动会造成整个table的重新布局
- 让多次重排的元素脱离文档流比如动画,使用 position 属性的 fixed 值或 absolute 值
- 避免设置多层内联样式,降低样式选择器的复杂度。CSS 选择符从右往左匹配查找,因避免节点层级过多。
- 将动画效果应用到position属性为absolute或fixed的元素上,避免影响其他元素的布局,这样只是一个重绘,而不是回流。
- CSS3 硬件加速(GPU加速),使用css3硬件加速,可以让transform、opacity、filters这些动画不会引起回流重绘 。
- 使用flexbox替代老的布局模型:老的布局模型以相对/绝对/浮动的方式将元素定位到屏幕上,而Floxbox布局模型用流式布局的方式将元素定位到屏幕上。
javaScript
- 避免频繁操作样式,最好一次性重写style属性,或者将样式列表定义为class并一次性更改class属性
- 避免频繁操作DOM,创建一个documentFragment,在它上面应用所有DOM操作,最后再把它添加到文档中
- 避免连续的强制的同步布局发生:不要在循环内获取dom 的样式例如:offsetWidth, offsetHeight, clientWidth, clientHeight… 这些。
- 避免频繁读取会引发回流/重绘的属性(offsetWidth, offsetHeight等),如果确实需要多次使用,就用一个变量缓存起来。
- 动画实现使用requestAnimationFrame:保证callback函数在每帧动画开始的时候执行