重绘(Repaint)和回流/重排(Reflow)

1 浏览器重绘与重排的区别?

  • 重排/回流(Reflow):当DOM的变化影响了元素的几何信息,浏览器需要重新计算元素的几何属性,将其安放在界面中的正确位置,这个过程叫做重排。表现为重新生成布局,重新排列元素。
  • 重绘(Repaint): 当一个元素的外观发生改变,但没有改变布局,重新把元素外观绘制出来的过程,叫做重绘。表现为某些元素的外观被改变

单单改变元素的外观,肯定不会引起网页重新生成布局,但当浏览器完成重排之后,将会重新绘制受到此次重排影响的部分

重排和重绘代价是高昂的,它们会破坏用户体验,并且让 UI 展示非常迟缓,而相比之下重排的性能影响更大,在两者无法避免的情况下,一般我们宁可选择代价更小的重绘。

『重绘』不一定会出现『重排』,『回流』必然会出现『重绘』。回流所需的成本比重绘高的多,改变父节点里的子节点很可能会导致父节点的一系列回流。

以下几个动作可能会导致性能问题: 改变 window 大小 改变字体 添加或删除样式 文字改变 定位或者浮动 盒模型 并且很多人不知道的是,重绘和回流其实也和 Eventloop 有关。 当 Eventloop 执行完 Microtasks 后,会判断 document 是否需要更新,因为浏览器是 60Hz 的刷新率,每 16.6ms 才会更新一次。 然后判断是否有 resize 或者 scroll 事件,有的话会去触发事件,所以 resize 和 scroll 事件也是至少 16ms 才会触发一次,并且自带节流功能。 判断是否触发了 media query 更新动画并且发送事件 判断是否有全屏操作事件 执行 requestAnimationFrame 回调 执行 IntersectionObserver 回调,该方法用于判断元素是否可见,可以用于懒加载上,但是兼容性不好 更新界面 以上就是一帧中可能会做的事情。如果在一帧中有空闲时间,就会去执行 requestIdleCallback 回调。

既然我们已经知道了重绘和回流会影响性能,那么接下来我们将会来学习如何减少重绘和回流的次数。

2 如何触发重排和重绘?

任何改变用来构建渲染树的信息都会导致一次重排或重绘:

  • 添加、删除、更新 DOM 节点
  • 通过 display: none 隐藏一个 DOM 节点-触发重排和重绘
  • 通过 visibility: hidden 隐藏一个 DOM 节点-只触发重绘,因为没有几何变化
  • 移动或者给页面中的 DOM 节点添加动画
  • 添加一个样式表,调整样式属性
  • 用户行为,例如调整窗口大小,改变字号,或者滚动。

3 如何避免重绘或者重排?

  1. 集中改变样式,不要一条一条地修改 DOM 的样式。

  2. 不要把 DOM 结点的属性值放在循环里当成循环里的变量。

for(let i = 0; i < 1000; i++) {
   // 获取 offsetTop 会导致回流,因为需要去获取正确的值
   console.log(document.querySelector('.test').style.offsetTop)
}
  1. 为动画的 HTML 元件使用 fixedabsoultposition,那么修改他们的 CSS 是不会 reflow 的。

  2. 不使用 table 布局。因为可能很小的一个小改动会造成整个 table 的重新布局。

  3. 尽量只修改position:absolutefixed元素,对其他元素影响不大

  4. 动画开始GPU加速,translate使用3D变化

  5. 动画实现的速度的选择,动画速度越快,回流次数越多,也可以选择使用 requestAnimationFrame

  6. 提升为合成层:将频繁重绘或者回流的节点设置为图层,图层能够阻止该节点的渲染行为影响别的节点。比如对于 video 标签来说,浏览器会自动将该节点变为图层。

    将元素提升为合成层有以下优点:

    • 合成层的位图,会交由 GPU 合成,比 CPU 处理要快
    • 当需要 repaint 时,只需要 repaint 本身,不会影响到其他的层
    • 对于 transform 和 opacity 效果,不会触发 layout 和 paint

    提升合成层的最好方式是使用 CSS 的 will-change 属性:

    #target {
      will-change: transform;
    }
    

    还有 video、iframe 标签

  7. 使用 transform 替代 top

  8. 使用 visibility 替换 display: none ,因为前者只会引起重绘,后者会引发回流(改变了布局)

  9. CSS 选择符从右往左匹配查找,避免节点层级过多

    关于合成层的详解请移步无线性能优化:Compositeopen in new window

思考题

思考题:在不考虑缓存和优化网络协议的前提下,考虑可以通过哪些方式来最快的渲染页面,也就是常说的关键渲染路径,这部分也是性能优化中的一块内容。 首先你可能会疑问,那怎么测量到底有没有加快渲染速度呢

img

当发生 DOMContentLoaded 事件后,就会生成渲染树,生成渲染树就可以进行渲染了,这一过程更大程度上和硬件有关系了。 提示如何加速: 从文件大小考虑 从 script 标签使用上来考虑 从 CSS、HTML 的代码书写上来考虑 从需要下载的内容是否需要在首屏使用上来考虑

上次更新:
Contributors: jiangjingmin