重排与重绘

时间:2025-02-07 06:57:34

重排

当DOM的变化影响了元素的几何信息(元素的的位置和尺寸大小),使得部分渲染树(或者整个渲染树)需要重新分析并且节点尺寸需要重新计算,表现为重新生成布局,重新排列元素,并将其安放在界面中的正确位置,这个过程叫做重排。

下面情况会发生重排:

  • 页面初始渲染,这是开销最大的一次重排
  • 添加/删除可见的DOM元素
  • 改变元素位置
  • 改变元素尺寸,比如边距(margin)、填充(padding)、边框(border)、宽度和高度等
  • 改变元素内容,比如文字数量,图片大小等
  • 改变元素字体大小
  • 改变浏览器窗口尺寸,比如resize事件发生时
  • 激活CSS伪类(例如::hover)
  • 通过display:none隐藏一个DOM节点
  • 给页面中的DOM节点添加动画

局部重排发生的情况: 把一个DOM元素的宽高等几何特性定死,然后改变该元素内部的几何属性,则会触发局部重排

重绘

由于节点的样式发生改变,例如改变元素背景色时,屏幕上的部分内容需要更新,表现为某些元素的外观被改变,但没有改变布局。除此之外,重排也必然会触发重绘。

除了重排触发重绘,下面情况会发生重排:

  • 改变颜色
  • 通过visibility:hidden隐藏元素
  • 改变border-style
  • 添加圆角、阴影
  • outline

如何避免重排重绘

减少重排的范围
尽量以局部布局的形式组织html结构,尽可能小的影响重排的范围。比如触发BFC,改变BFC内部的元素,不会影响到外部。

减少重排重绘的次数
1. 集中改变样式
通过改变class的方式来集中改变样式
2. 将 DOM 离线
1)使用display:none
一旦我们给元素设置 display:none 时(只有一次重排重绘),元素便不会再存在在渲染树中,相当于将其从页面上“拿掉”,我们之后的操作将不会触发重排和重绘,添加足够多的变更后,通过 display:block属性显示(另一次重排重绘)。通过这种方式即使大量变更也只触发两次重排重绘。
2)使用documentFragment
创建一个游离于DOM树之外的节点,然后在此节点上批量操作,最后插入DOM树中,只触发一次重排重绘
3. 提升为合成层
1)合成层的位图,会将于GPU合成,比CPU快
2)当需要重排重绘时,只影响本身,不影响其他层
3)对于transformopacity,不会触发重排和重绘,只会触发合成
4)如何提升为合成层
css的will-change属性

#target {
	will-change: transform
	}

4. 分离读写操作(拓展)
浏览器的渲染队列机制: 当我们修改了元素的几何属性,导致浏览器触发重排或重绘时。它会把该操作放进渲染队列,等到队列中的操作到了一定的数量或者到了一定的时间间隔时,浏览器就会批量执行这些操作。
但有些请求会强制刷新渲染队列,使得立即执行重排重绘,比如

  • offsetTop, offsetLeft, offsetWidth, offsetHeight
  • scrollTop, scrollLeft, scrollWidth, scrollHeight
  • clientTop, clientLeft, clientWidth, clientHeight
  • getComputedStyle(), 或者 IE的 currentStyle
// bad 强制刷新 触发四次重排+重绘
div.style.left = div.offsetLeft + 1 + 'px';
div.style.top = div.offsetTop + 1 + 'px';
div.style.right = div.offsetRight + 1 + 'px';
div.style.bottom = div.offsetBottom + 1 + 'px';


// good 缓存布局信息 相当于读写分离 触发一次重排+重绘
var curLeft = div.offsetLeft;
var curTop = div.offsetTop;
var curRight = div.offsetRight;
var curBottom = div.offsetBottom;

div.style.left = curLeft + 1 + 'px';
div.style.top = curTop + 1 + 'px';
div.style.right = curRight + 1 + 'px';
div.style.bottom = curBottom + 1 + 'px';

上面会触发四次重排重绘,而下面只会触发一次重排重绘。