回流(重排)与重绘 —— 什么是回流和重绘,造成的原因是什么,如何去减少?

浏览器的渲染原理

浏览器的渲染原理步骤:

  • 解析文档(HTML、SVG、XHTML),生成 DOM Tree
  • 解析CSS,生成 CSSOM Rule Tree
  • 根据 DOM Tree 和 CSSOM Rule 生成 Rendering Tree
  • 布局
  • 绘制

具体参考:

浏览器渲染原理、浏览器的解析

“回流 / 重排”和“重绘”的定义

①、回流的定义 :当Rendering Tree 中部分元素的尺寸大小、布局、隐藏等属性改变时,浏览器的布局需要调整,则需要重新渲染DOM。这个过程就叫回流。回流也叫重排(对整个页面进行重新排版)。

②、重绘的定义:当元素属性的改变不影响DOM Tree的结构,即不会影响浏览器的布局,只是“表象”发生变化(如background-color,visibility等),那么针对新样式对元素进行重新绘制

③、二者的联系

  • 回流一定会引起重绘,重绘不一定引起回流。
  • 回流需要重新渲染DOM,也要重新处理Rendering Tree。即回流的开销比重绘要大。

何时发生回流

比较好理解的,DOM元素发生变化:

  • 添加或删除可见元素
  • 元素的位置改变
  • 元素的尺寸大小改变(width,height,padding,border,margin等)

还有一类情况,即访问某些属性或调用某些方法或resize事件发生时

属性

  • offsetTop、offsetLeft、offsetWidth、offsetHeight
  • scrollTop、scrollLeft、scrollWidth、scrollHeight
  • clientTop、clientLeft、clientWidth、clientHeight

方法:

  • getComputedStyle()
  • getBoundingClientRect()
  • currentStyle()

事件:

  • 浏览器窗口尺寸改变,即resize事件
  • 页面初始化渲染

访问这些属性或调用这些方法,都是关乎元素在浏览器中的坐标位置,为了能够获得精确的值,浏览器需要进行即时计算

嗯哼?即时计算?那就是要回流了。因为回流就是要计算元素布局位置构建DOM。

也就是说:当访问这些属性,或者调用这些方法,为了即时获得准确结果,浏览器干脆就重新计算元素位置大小,即发生一次回流!所以访问这些属性,调用这些方法,也会引起回流!

另外,resize事件,让浏览器窗口尺寸发生变化。而浏览器正是根据浏览器窗口大小来计算元素的位置和大小。那么浏览器的窗口大小发生变化,浏览器也需要重新去计算,那么也会发生回流。

总结:

关于DOM元素

  • 添加或删除可见元素
  • 元素位置发生变化
  • 元素的尺寸大小发生变化
  • 页面初始化渲染

访问特定属性

  • offsetTop、offsetLeft、offsetWidth、offsetHeight
  • scrollTop、scrollLeft、scrollWidth、scrollHeight
  • clientTop、clientLeft、clientWidth、clientHeight

调用特定方法

  • getComputedStyle()
  • getBoundingClientRect()
  • currentStyle()

特定事件发生

  • 浏览器窗口尺寸改变,即resize事件
  • 页面初始化渲染

何时发生重绘

引起重绘的因素比较单一 :仅仅改变的是某元素的外观或者说风格,不影响整个页面的布局,不会影响DOM。

*** 如何减少回流 / 重绘

一、 避免逐次改变样式,样式统一批量修改

如果是一个属性一个属性改,那么就会引起多次回流:

const ele = document.getElementById('test');
ele.style.width = '100px';
ele.style.height = '200px';
ele.style.border = '1px solid black'

引起三次回流。

解决① 可以合并到一个类里,统一添加:

adding-style{
	width: 100px;
	height: 200px;
	border: 1px solid red;
}
const ele = document.getElementById('test');
ele.classList.add('adding-style');

解决② 使用cssText

const ele = document.getElementById('test');
ele.style.cssText += 'width: 100px; height: 200px; border: 1px solid red;';

二、 使DOM脱离文档流,再作批量处理

若要对DOM元素作多次处理修改,不作任何处理的话则会引起多次回流;

处理思路:

  • 使DOM元素脱离文档流
  • 对DOM元素作多次修改
  • 修改完,再把DOM元素带回文档流

具体实现:

解决① display:none、bababa处理、display:block

在进行一顿操作之前,先把DOM元素隐藏起来,操作完再让它显示出来。尽管隐藏和恢复显示会有2次回流,但至少隐藏之后不会因为一顿操作引起再多的回流。

const ele = document.getElementById('test');
ele.style.display = 'none';
// 隐藏成功,已脱离文档流,开始操作
// bababa
// bababa
// bababa
// 操作完成,可以加回文档流
ele.style.display = 'block';

解决② 使用document fragment在DOM Tree之外建立一个子树

const ele = document.getElementById('test');
const fragment = document.createDocumentFragment();
// 对子树 fragment操作
// bababa
// bababa
// bababa
// 添加回去
ele.appendChild(fragment);

解决③ 将待处理元素拷贝至一个脱离文档流的节点,处理完再代替原节点

const ele = document.getElementById('test');
const cloneNode = ele.cloneNode(true);
// 处理脱离文档流的节点cloneNode
// bababa
// bababa
// bababa
// 拿cloneNode代替原节点
ele.parentNode.replaceChild(cloneNode,ele);

三、 缓存布局属性(需即时计算的属性)

上面说过,当访问特定属性,浏览器为保证精确性、即时性,会再一次计算元素,从而引发回流。

故可以把这些属性缓存起来再使用。

若不缓存,访问一次则引发一次回流:

const ele = document.getElementById('test');
ele.style.left = ele.offsetLeft + 1 + 'px';  //回流
ele.style.top = ele.offsetTop + 1 + 'px';    //回流

缓存到变量:

const ele = document.getElementById('test');
var curLeft = ele.offsetLeft;  // 缓存到变量
var curTop = ele.offsetTop;    // 缓存到变量

ele.style.left = curLeft + 1 + 'px';  //不会回流
ele.style.top = curTop + 1 + 'px';    //不会回流

四、 position属性控制其脱离文档流

position可以设置absolute、fixed,分别对应的是绝对定位和固定定位。

这两种定位都会使得元素脱离文档流

那么把会多次回流的元素,将其设置为position:absolute或position:fixed

脱离文档流,则发生变化也不会再引起回流。

五、 CSS3硬件加速

能够触发CSS3硬件加速的属性,彻底不会引起回流:

  • transform
  • opacity
  • filters
  • 1
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
回流(reflow)和重绘(repaint)是浏览器渲染页面时的两个重要概念。 回流是指当 DOM 的变化影响了元素的布局,需要重新计算元素的位置和大小,然后重新构建渲染树,最后进行绘制的过程。回流可能会导致一些性能问题,因为它需要计算布局,这是一项非常耗费资源的操作。触发回流的操作包括: - 添加、删除或修改 DOM 元素 - 改变元素的位置、大小 - 改变元素的内容、文字 - 改变窗口大小或滚动 - 修改样式(尤其是影响元素盒模型的样式,例如 width、height、padding、margin 等) 重绘是指当渲染树中的元素样式改变,但没有影响它们的布局时,浏览器只需要重新绘制这些元素的内容,而不需要重新计算它们的位置和大小。重绘回流的开销要小一些,但也需要花费一定的时间。触发重绘的操作包括: - 改变元素的颜色、背景色、边框颜色等 - 改变元素的文本颜色、字体大小等 - 添加、删除、改变元素的阴影、透明度等 因此,为了提高页面性能,我们应该尽可能地减少回流重绘的次数。可以采取的措施包括: - 避免频繁读取和修改 DOM 元素的样式和属性,可以使用缓存或批量修改的方式来优化 - 使用 CSS3 动画代替 JavaScript 实现动画效果,因为 CSS3 动画可以利用硬件加速,而 JavaScript 实现的动画会导致频繁的回流重绘 - 将元素的 position 属性设置为 fixed 或 absolute,因为这些元素不会影响其他元素的布局,可以减少回流的次数 - 将元素的 opacity 属性设置为 0 或 1,而不是使用 visibility:hidden 或 display:none,因为前者只会触发重绘,而后者会触发回流
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Silam Lin

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值