CSS 谈谈你对重排和重绘的理解

一、前言

   当我们给我们的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)

扫描二维码关注公众号,回复: 15294264 查看本文章

(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操作进行批量处理,减少重排的次数,从而提升页面的性能。

猜你喜欢

转载自blog.csdn.net/qq_63299825/article/details/131040727