一、前言
当我们给我们的DOM结构改变或者给DOM结构设置样式时,会触发回流和重绘,但不同的样式改变,是否触发重排和重绘是不确定的。我们有必要深度理解重排和重绘,通过减少重排可以提高性能。
了解浏览器的解析渲染机制:
(1).解析HTML,生成DOM树,解析CSS,生成CSSOM树
(2).将DOM树和CSSOM树结合,生成渲染树(Render Tree)
(3).Layout(回流):根据生成的渲染树,进行回流(Layout),得到节点的几何信息(位置,大小)
(4).Painting(重绘):根据渲染树以及回流得到的几何信息,得到节点的绝对像素
(5).Display:将像素发送给GPU,展示在页面上
二、重排(回流)
当DOM的改变影响元素的几何信息(大小,位置),浏览器重新计算元素的几何信息(位置),将其正确的显示在浏览器上,这个过程就叫重排,也叫重绘。
什么情况下会触发重排?
(1).添加或删除可见的DOM元素(display:none;display:block)
(2).元素的尺寸发生变化 (宽度width和高度height,外边框margin,内边框padding)
(3).元素的内容发生变化 也会导致元素的几何信息发生变化
(4).元素的位置 (通过定位调整元素的位置)
(5).页面一开始渲染的时候
(6).浏览器的窗口尺寸变化
除此之外,当我们获取一些特定的值时,也会触发浏览器进行回流,如下:
offsetTop、offsetLeft、 offsetWidth、offsetHeight、scrollTop、scrollLeft、scrollWidth、scrollHeight、clientTop、clientLeft、clientWidth、clientHeight, 除此还包括getComputedStyle
方法,原理是一样的
三、重绘
元素的外观被改变时,但是不影响元素的排列分布,浏览器将其重新绘制的过程,就叫重绘,如一些css样式,color, 背景色,以及文本方向的修改,阴影的修改
四、如何减少重排?
(1).如果想设定元素的样式,通过改变元素的 class 类名 (尽可能在 DOM 树的最里层)
(2).避免设置多项内联样式
(3).应用元素的动画,使用 position 属性的 fixed 值或 absolute 值(如前文示例所提)
(4).避免使用 table 布局,table 中每个元素的大小以及内容的改动,都会导致整个 table 的重新计算
(5).对于那些复杂的动画,对其设置 position: fixed/absolute,尽可能地使元素脱离文档流,从而减少对其他元素的影响
(6).使用css3硬件加速,可以让transform、opacity、filters这些动画不会引起回流重绘
(7).避免使用 CSS 的 JavaScript 表达式
示例1:当我们要对元素的样式进行改变时,可以先将元素利用 display:none 将元素隐藏,减少浏览器重排的次数,减少额外的性能消耗
<div></div>
<script>
const div = document.querySelector('div');
// 先将元素隐藏
div.style.display = 'none';
div.style.width = '100px';
div.style.height = '100px';
div.style.border = '10px solid red';
div.style.background = 'red';
// 样式设置完毕
div.style.display = 'block';
</script>
示例2.当我们动态插入多个节点时,每一次插入节点都会触发一次浏览器的回流,如果我们使用DocumentFragment虚拟节点,就可以减少浏览器的回流次数,总而保证浏览器的性能
<div></div>
<script>
const div = document.querySelector('div');
const fruits = ['Apple', 'Orange', 'Banana', 'Melon'];
// 创建了虚拟DOM节点
const fragment = document.createDocumentFragment();
fruits.forEach((fruit) => {
const li = document.createElement('li');
li.innerHTML = fruit;
fragment.appendChild(li);
});
// 一次性将节点插入到 DOM div的后面
div.appendChild(fragment);
</script>
运行结果
新增的DOM结果已经添加到div后面
示例3:通过类名合并样式 也可以减少重排的次数.
未合并样式之前,通过js 依次对样式进行操作,每操作一次,就有可能进行一次回流或者重绘
<div></div>
<script>
const div = document.querySelector('div');
// 先将元素隐藏
div.style.width = '100px';
div.style.height = '100px';
div.style.border = '10px solid red';
div.style.background = 'red';
// 样式设置完毕
</script>
当我们合并样式,通过操作类名 来设置样式。只进行一次重排和重绘就达到了效果
<style>
.active {
width: 100px;
height: 100px;
background: red;
border: 10px solid red;
}
</style>
<div></div>
<script>
const div = document.querySelector('div');
div.style.background;
</script>
五、重排和重绘的区别
重排是一种更加耗费性能的操作,因为它需要对元素的几何属性、布局信息和文本流进行重新计算;而重绘则是一种相对较少耗费性能的操作,因为它只需要重新绘制元素的样式即可。因此,尽量避免在页面中频繁进行重排操作,可以通过对多个DOM操作进行批量处理,减少重排的次数,从而提升页面的性能。