我们根据渲染的流程可知, 回流一定会触发重绘,而重绘不一定会回流。
渲染性能优化
回流和重绘的代价是比较昂贵的,渲染性能优化,就是要尽可能减少Layout回流和Paint重绘发生的次数,将回流和重绘的影响范围限制在单独的图层之内
1、合并多次布局操作
我们可以合并多次对DOM和样式的修改,然后一次处理掉,以此来最小化回流和重绘操作,比如:
// bad
const el = document.getElementById('test');
el.style.margin = '5px';
el.style.width = '100px';
el.style.borderRight = '2px';
例子中,有三个样式属性被修改了,每一个都会影响元素的几何结构,引起回流。(当然,大部分现代浏览器都对其做了优化,只会触发一次。但是如果在旧版的浏览器或者在上面代码执行的时候,有其他代码访问了布局信息,那么就会导致三次回流)
我们合并所有的布局操作,然后统一处理,比如这样:
// good
const el = document.getElementById('test');
el.style.cssText += 'margin: 5px;width: 100px;border-right: 2px; '
2、减少或避免强制同步布局
上面我们提到,访问一些属性(就是 offsetWidth 那一堆属性)会导致浏览器强制清空队列,进行强制同步布局。实际使用中可以尽量避免,如果不能避免,也应该减少。
比如我们想批量将一些标签的宽度设为某个box的宽度,我们可能会写成下面这样:
// bad
for (let i = 0; i < elment.length; i++) {
elment[i].style.width = box.offsetWidth + 'px';
}
这段代码看上去问题不大,但是在每次循环的时候,都会去读取box的 offsetWidth ,导致浏览器每次都会因强制同步布局而触发回流,造成了很大的性能问题。
类似这这情况,我们可以把读取到的 offsetWidth 进行缓存:
// good
const width = box.offsetWidth;
for (let i = 0; i < element.length; i++) {
element[i].style.width = width + 'px';
}
3、使用 transform 和 opacity 来实现动画
最佳的性能渲染流程,就是直接避开回流和重绘,只运行 Composite 合成这一操作。
目前可以有合成器单独处理的属性有两个:
transforms 和 opacity
比如我们可以使用 translate 代替 left 、 top 。
使用 opacity 代替 visibility 等
4、简化绘制的复杂度、减小绘制区域
除 transform 或 opacity 属性之外,更改任何属性始终都会触发绘制。
绘制通常是像素管道中开销最大的部分;应尽可能避免绘制。
4.1 通过层的提升来减少绘制区域
绘制并非总是绘制到内存中的单个图像。事实上,在必要时浏览器可以绘制到多个图像或合成器层,各个层可以在彼此的上面处理并合成,以创建最终图像。
创建新层的最佳方式是使用 will-change CSS 属性。
此方法在 Chrome、Opera 和 Firefox 上有效,并且通过 transform 的值将创建一个新的合成器层:
.moving-element {
will-change: transform;
}
对于不支持 will-change 但受益于层创建的浏览器,例如 Safari 和 Mobile Safari,需要开启GPU加速来强制创建一个新层:
.moving-element {
transform: translateZ(0);
}
4.2 优化或减少动画编排
减少绘制区域往往是编排您的动画和变换,使其不过多重叠,或设法减少动画编排,避免对页面的某些部分设置动画。
4.3 降低绘制的复杂性
一些css属性的绘制比其他绘制的开销更大。例如,绘制任何涉及模糊(例如 shadow )的元素所花的时间将比绘制一个 border 的时间要长。
实际开发中,我们要确定可否使用一组开销更小的样式,或者替代方式来实现最终结果。
5、让复杂的布局“离线”
对于复杂的动画,或者频繁触发回流的元素,我们
创建一个 documentFragment 或 div ,在它上面应用所有DOM操作,最后再把它添加到 window.document 。
也可以在一个 display:none 的元素上进行操作,最终把它显示出来。因为 display:none 上的DOM操作不会引发回流和重绘。
也可以使用绝对定位,让它脱离文档流,从而避免引起父元素以及后续元素的频繁回流。
6、其他
6.1 避免使用table布局
我们已经在上面说过, <table> 的计算需要不止一次的遍历,table是可以影响之前已经进入的DOM元素的显示的元素。即使一些小的变化和会导致table中所有其他节点回流。
网友评论