重排和重绘浅析

109 阅读4分钟

在HTML中,每一个元素都可以理解为一个盒子,在浏览器解析过程中,会涉及重排和重绘。

重排(reflow):也叫回流,布局引擎会根据各种样式计算每个盒子在页面上的大小和位置。

重绘(repaint):当计算好盒子的位置、大小和其他属性之后,浏览器会根据每个盒子的特性进行绘制。

浏览器的具体实现:

Snipaste_2023-09-22_11-42-12.png

1、解析HTML,生成DOM树;

2、解析CSS,生成CSSOM树;

3、将DOM树和CSSOM树结合,生成渲染树(Render Tree);

4、生成布局(Layout):根据生成的渲染树,进行重排,得到节点的几何信息(位置和大小);

5、绘制(Painting):重绘,根据渲染树和重排得到的几何信息,得到节点的绝对像素;

6、展示(Display):将像素发送给 GPU,展示在页面上。

在页面初始的渲染阶段,不可避免的触发重排,可以理解为页面一开始为空白的元素,后面添加了新的元素使得页面的布局发生改变。

当我们对DOM的修改引起了DOM几何尺寸的变化(修改元素的宽高、隐藏属性等),浏览器需要重新计算元素的几何尺寸。

但我们对DOM的修改导致了样式的改变(color和填充颜色),却并未影响其几何元素时,浏览器不需要重新计算元素的几何尺寸,那么仅仅触发了重绘。

可见,重绘不一定触发重排,但是重排一定触发重绘。不管页面发生重绘或者重排,都会影响性能。因此要想办法减少重绘和重排的次数

重排

触发时机: 当页面几何信息和布局发生变化的时候,就会发生重排。

影响: 由于DOM的结构发生了改变,因此从生成DOM这一步开始,要经历 样式计算、生成布局树、建立图层树、再到生成绘制列表以及之后的显示器显示这整个渲染过程都需要走一遍,开销是非常大的。

触发情况: 1、元素的位置发生改变 2、元素的尺寸发生改变(包括外边距、内边距、边框大小、高度和宽度) 3、添加或者删除可见的DOM元素 4、页面一开始渲染阶段(不可避免) 5、浏览器的窗口尺寸变化(因为重排是根据视口的大小来计算元素的位置和大小的) 6、内容发生变化,如何文本变化或图片被另一个不同尺寸的图片所替代 7、获取一些特定属性的值,浏览器为了获取这些值,也会进行重排操作。如下列,这些值都是通过即时计算得到,因此浏览器为了获得这些值也会进行重排。

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

重绘

当一个元素的外观发生改变,但没有改变布局,重新把元素的外观绘制出来的过程,叫做重绘。

通过构造渲染树和重排阶段,我们知道了那些节点是可见的,以及可见节点的样式和具体的几何信息(元素在视口内的位置和几何尺寸大小),接下来就可以将渲染树的每个节点都转换为屏幕上的实际象素,这个阶段叫做重绘。

仅触发重绘的操作:

1、当DOM的修改导致了样式的变化,并且没有影响几何属性的时候,会导致重绘(repaint)。 2、重绘过程: 因为没有导致DOM的变化,因此元素的位置信息不需要更新,所以当发生重绘的时候,会跳过生成布局树和建立图层树的过程,直接到后续的生成绘制列表,然后后续一系列操作。

如何避免

  1. 避免频繁使用 style,而是采用修改class的方式。
  2. 将动画效果应用到position属性为absolutefixed的元素上。
  3. 批量操作 DOM,比如读取某元素 offsetWidth 属性存到一个临时变量,再去使用,而不是频繁使用这个计算属性;又比如利用 document.createDocumentFragment() 来添加要被添加的节点,处理完之后再插入到实际 DOM 中
  4. 也可以先为元素设置display: none,操作结束后再把它显示出来。因为在display属性为none的元素上进行的DOM操作不会引发回流和重绘
  5. 对于 resizescroll 等进行防抖/节流处理。
  6. 避免频繁读取会引发回流/重绘的属性,如果确实需要多次使用,就用一个变量缓存起来。
  7. 利用 CSS3 的transformopacityfilter这些属性可以实现合成的效果,也就是CPU加速。