面试题总结:说一下对回流和重绘的理解

关于回流和重绘,需要先了解浏览器渲染页面的过程。

面试题总结:说一下对回流和重绘的理解

  1. 解析HTML,生成DOM树,解析CSS,生成CSSOM样式结构体
  2. 将DOM树和CSSOM结合,生成渲染树(Render Tree)
  3. Layout(回流):根据生成的渲染树,进行回流(Layout),得到节点的几何信息(位置,大小)
  4. Painting(重绘):根据渲染树以及回流得到的几何信息,得到节点的绝对像素
  5. Display:将像素发送给GPU,展示在页面上。

1. 什么是回流?

当渲染树中的一部分(或全部)因为元素的规模尺寸,布局,隐藏等改变而需要重新构建的过程叫回流(改变大小、布局)。

2. 什么是重绘?

当渲染树中的一部分元素需要更新属性,如改变元素的外观、风格,而不影响布局的重新渲染的过程叫重绘(改变样式)。

注意:

  • 每个页面至少需要一次回流+重绘;
  • 回流必将引起重绘,而重绘不一定会引起回流;

3. 回流发生条件

当页面布局和几何属性改变时,如以下:

1、添加或者删除可见的DOM元素;

2、元素位置改变;

3、元素尺寸改变——边距、填充、边框、宽度和高度

4、内容改变——比如文本改变或者图片大小改变而引起的计算值宽度和高度改变;

5、页面渲染初始化;

6、浏览器窗口尺寸改变——resize事件发生时;

4. 重绘发生条件

元素的属性或样式发生改变,如background-color改变等。

回流的开销很大,如果每次操作都进行回流和重绘,消耗相当大,那么如何进行性能优化?

5. 浏览器的优化机制

大多数浏览器都会通过队列化修改并批量执行来优化重排过程。

浏览器会把所有会引起回流、重绘的操作都放入一个队列,等过一定的时间或者操作达到了一定的阈值,浏览器就会flush队列,进行一个批处理。这样让多次的回流和重绘变成一次回流重绘。
但是,当获取布局信息操作的时候,会强制浏览器提前flush队列,触发回流重绘返回信息,这种情况下,浏览器的优化就不起作用了。比如以下属性或方法:

  • offsetTop、offsetLeft、offsetWidth、offsetHeight
  • scrollTop、scrollLeft、scrollWidth、scrollHeight
  • clientTop、clientLeft、clientWidth、clientHeight
  • getComputedStyle()
  • getBoundingClientRect

那么,除了浏览器的优化,我们还有什么方法可以来减少回流和重绘达到性能优化?

6. 减少回流和重绘的方法

1. 通过合并多次DOM样式的修改,来减少回流和重绘的发生次数。

利用className修改元素的class

const el = document.getElementById(‘test’);
el.className += ’ active’;

利用cssText合并样式修改操作

const el = document.getElementById(‘test’);
el.style.cssText += ‘border-left: 1px; border-right: 2px; padding: 5px;’;

2. 批量修改DOM

使用文档片段(document fragment)在当前DOM之外构建一个子树,进行缓存操作,再把它拷贝回文档,这样引发一次回流和重绘;
先利用display:none隐藏元素,进行应用修改之后,再重新显示,这样就只引发两次回流和重绘;

3. 避免引起浏览器强制flush队列

如使用以上浏览器优化提到的属性或方法。

4. 对于有动画效果的元素,使用绝对定位让其脱离文档流,如position属性设为absolute或fixed

参考文章:

  1. 前端进阶(二)重绘和回流
  2. 你真的了解回流和重绘吗