性能优化之回流重绘

时间:2021-1-18 作者:admin

首先,我们需要先明白一个问题

用户请求的资源到达渲染引擎后如何工作?

  1. 构建DOM树
  2. 构建CSSOM树和样式计算
  3. 构建渲染树

下面是网上的一张图片,可以很清晰的说明前三步
性能优化之回流重绘

  1. 布局阶段

布局阶段会从渲染树的根节点开始遍历,然后确定每个节点对象在页面上的确切的大小和位置,所有相对的测量值也都会被转换为屏幕内的绝对像素值。

  1. 绘制

浏览器主进程将默认的图层(普通文档流的元素)和多个独立图层交给GPU进程,GPU进程再将各个图层合成,最终显示在屏幕上。
哪些元素会独立图层? <video><iframe><canvas>等。
下面是一张网站的图层图片,可见,所有元素并不是都在一个图层上展示。

性能优化之回流重绘 理解了网页渲染页面的过程,也明白了图层,下面来看一下什么是回流?什么是重绘?

什么是重绘?

当渲染树中的一些元素需要更新属性,而这些属性只是影响元素的外观,风格,而不会影响布局的,比如background-color。则就叫称为重绘。

哪些元素会导致重绘?

Color
Border-style
Border-radius
Visibility
Text-decoration
Background
Background-img
Outline-color
Box-shadow
……

什么是回流?

当渲染树中的一部分(或全部)因为元素的规模尺寸,布局,隐藏等改变而需要重新构建。这就称为回流。每个页面至少需要一次回流,就是在页面第一次加载的时候,这时候是一定会发生回流的,因为要构建渲染树。
引发问题:回流的代价要远高于重绘,回流会造成重绘,重绘不一定回流。回流浏览器需要重新渲染页面,从而影响性能。

哪些因素会导致回流?

  • 添加或删除可见的元素
  • 元素位置改变
  • 元素尺寸改变:边距,填充,边框,宽度和高度
  • 页面渲染初始化
  • 浏览器窗口尺寸改变:resize 事件发生时
  • 获取元素的偏移量属性,例如:
    scrollTop、scrollLeft、scrollWidth、offsetTop、offsetLeft、offsetWidth、offsetHeight,浏览器为了保证值的正确性,会回流取最新的值
    等等…

哪些元素会导致回流?

  • 与盒子模型有关的属性
width 
Height
Padding
Margin
Display
Border-width
Border 
  • 与位置和浮动有关的
width 
Height
Padding
Margin
Display
Border-width
Border 
  • 与文字和行间距等等有关的
width 
Height
Padding
Margin
Display
Border-width
Border 

我们既然明白了什么是重绘和回流,以及哪些因素和元素导致了回流和重绘,并且明白了重绘回流在页面渲染过程中造成哪些影响,那我们如何优化呢?

浏览器渲染层面如何优化?

主要从下面两个方面来避免发生重绘回流

  1. 将频繁回流的DOM元素作为一个独立的图层,那么这个DOM元素的回流只影响这一个图层,其他图层不会受影响。
  2. 避免使用触发回流的css属性。

优化举例

  • 对于动画新建图层,对于频繁变化的元素应该为其加一个 transform 属性, transform:
    translateZ(0px);使用这个属性会开启GPU加速,浏览器会认为这种情况重绘比较频繁,默认会单独变成一个图层。
  • 对于视频使用video 标签。
  • gif图会频繁触发重绘,但是它在标签中,所以没有独立图层,我们可以将其独立出来图层,这样可以使重绘的范围变小。
  • 不要使用table布局,可能很小的一个小改动会造成整个table的重新布局
  • 不要逐条的修改DOM的样式,如果想修改一个DOM节点的多个css属性值,可以预先定义好class,一次性修改
  • 不要把DOM节点的属性值放在一个循环里当成循环里的变量
  • 使用translate替代top

下面拿几个具体的例子来展示:

1. 多次改变DOM元素的属性,并不会像之前说的那样,引发多次回流重绘,而是将这些合并为一次,因为浏览器中有缓冲机制,通过这个缓冲机制,能够减少回流重绘的次数,比如控制在100ms周期内,多次回流重绘合并为一次,这属于浏览器自身的性能优化。
性能优化之回流重绘性能优化之回流重绘
由上面结果可以看出,多次改变DOM的属性,并不会发生多次回流重绘,事实上,只导致了一次,但是我们还是要做自己代码的优化,尽量减少回流重绘的次数,我们不能让浏览器自身的机制来保证代码的性能,每个浏览器自身的优化性能也都不同,所以我们尽量使我们写出来的代码变得强壮。
2. 循环中获取offsetWidth
性能优化之回流重绘
结果如下:回流发生了100次,也就是获取多少次offsetTop,就发生了多少次回流,并且根据下面的图片可以看出,已经报红色了,因为这其中进行了100次的强制性回流,事实上,获取offsetTop这个属性的时候,已经不再遵循上面讲的浏览器自身性能优化的规律了,获取一次,会留一次。
性能优化之回流重绘
性能优化之回流重绘
为什么会导致这个问题呢?
因为我们在读取元素offsetTop之前,先在该元素中添加了一个新的元素,浏览器为了给我们最新的offsetTop值,需要重新布局来获取。
如果在获取值之前,不对该元素进行写的操作,会怎样呢?
性能优化之回流重绘
性能优化之回流重绘
事实上,读写分离后,值发生了一次回流。
抛开读写分离这件事,那上面的例子我们应该如何优化呢,可以将获取的offsetTop的值取出来之后作为缓存,不要每次循环都获取。
性能优化之回流重绘
性能优化之回流重绘
对于这个例子,也有人说,是因为每次循环中都需要读写DOM元素导致的性能降低,JS每次都读写DOM元素无疑会大大降低性能,但不会发生回流。

3. 合理利用GPU硬件加速

在做各种前端动画实现时,充分利用GPU加速,可以使动画更加流畅,用户体验更好,利用GPU这种硬件层次的加速,我们永远不用担心会触及它的上限,因为在触及上限前,一定先会触发浏览器的上限。

一些css3的属性会触发GPU加速:

  1. Translate:有translateX,translateY,translate3D等,主要用于处理图像的平移。
  2. Rotate:同样也有x,y,z轴的转换,主要用来处理图像的转换。
  3. Scale:主要用于处理图像的缩放。
  4. opacity:用于改变图像的透明度。
  5. Filter:滤镜。

上述的几大效果都会触发浏览器的GPU加速机制。在做动画时,尽量通过css3的动画代替传统的定时器轮询,用改变top/left/width/height值得方式来完成动画的渲染。这样既可以做出优美的动画,也不会对渲染性能造成影响。

4.将频繁引发页面回流的元素独立图层

重绘和回流对页面渲染期间的性能影响很大,动画的元素会频繁的触发页面的回流,为了减少动画元素对其他元素的影响,从而减少页面重绘回流的次数,我们需要将动画元素提升为独立层。

动画会频繁触发回流和重绘
性能优化之回流重绘
性能优化之回流重绘
独立图层后不再发生回流
性能优化之回流重绘
性能优化之回流重绘

既然将元素独立图层后不会发生回流重绘,或者说重绘的范围大大缩小,那么是不是只要将元素独立图层就一定能提高性能呢?

对于类似动画元素这种频繁引发页面回流重绘的元素来说,独立出来一个图层是能更好的提高渲染方面的性能,但是创建一个图层并不是免费的,它需要消耗额外的内存和管理资源。在内存资源有限的设备上,合成层带来的性能改善,可能远远赶不上过多合成层开销给页面性能带来的负面影响。比如一些较旧的手机和电脑,开启加速,无论是电池还是发热量都会比平时高。
同时,由于每个渲染层的纹理都需要上传到GPU处理,因此我们还需要考虑CPU和GPU之间的带宽问题,以及有多大内存供GPU处理这些纹理。

元素未独立图层
性能优化之回流重绘
性能优化之回流重绘
元素独立图层
性能优化之回流重绘
性能优化之回流重绘
以上文章为个人理解加上实践结果,如有理解不正确的地方,还请指正,感激不尽。

声明:本文内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎进行举报,并提供相关证据,工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。