面试题总结:说一下对回流和重绘的理解
关于回流和重绘,需要先了解浏览器渲染页面的过程。
- 解析HTML,生成DOM树,解析CSS,生成CSSOM样式结构体
- 将DOM树和CSSOM结合,生成渲染树(Render Tree)
- Layout(回流):根据生成的渲染树,进行回流(Layout),得到节点的几何信息(位置,大小)
- Painting(重绘):根据渲染树以及回流得到的几何信息,得到节点的绝对像素
- 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
参考文章: