重排重绘与合成

前端有个很经典的问题是说下重排和重绘的区别,一般我们会说重排性能低,而重绘性能高。但其实我们可以深入探究一下其中但原因。

重排(回流)

定义

当通过JS或者 CSS 修改元素的几何属性,例如改变元素的宽度、高度等,那么浏览器会触发重新布局,解析之后的一系列子阶段,这个过程就叫重排。无疑,重排需要更新完整的渲染流水线,所以开销也是最大的。

图示

image.png

触发条件

  • 添加或者删除可见的DOM元素
  • 元素位置改变
  • 元素尺寸改变
  • 元素内容改变(例: 一个文本被另一个不同尺寸的图片替代)
  • 页面渲染初始化(无法避免)
  • 浏览器窗口尺寸改变

优化方案

  1. 尽量不要在布局信息改变时做查询(会导致渲染队列强制刷新)。
  2. 合并多次DOM操作。比如用class来改变多个样式。
  3. 避免使用table。
  4. 使用fragment元素(createDocumentFragment
  5. 让元素脱离文档流。即让当前元素有自己的图层。
  6. 多次修改时把dom 离线 ,修改完再显示。(display:none)
  7. 使用采用虚拟DOM的库,如Vue,React
  8. will-change: transform 启用硬件加速

 

重绘

定义

当通过JS或者 CSS 修改元素的绘制属性,例如改变元素的背景颜色,那么布局阶段将不会被执行,因为并没有引起几何位置的变换,所以就直接进入了绘制阶段(即生成待绘制列表),然后执行之后的一系列子阶段,这个过程就叫重绘。相较于重排操作,重绘省去了布局和分层阶段,所以执行效率会比重排操作要高一些。

图示

image.png

触发条件

  • background属性(background,background-color,background-image,background-position,background-repeat,background-size)
  • outline属性(outline,outline-color,outline-style)
  • box-shadow属性
  • border属性(border-style,border-radius)
  • visibility

优化方案

  1. 合并多次操作

 

合成

定义

更改一个既不要布局也不要绘制的属性,渲染引擎将跳过布局和绘制,只执行后续的合成操作,我们把这个过程叫做合成。比如我们使用了 CSS 的 transform 来实现动画效果,这可以避开重排和重绘阶段,直接在非主线程上执行合成动画操作。这样的效率是最高的,因为是在非主线程上合成,并没有占用主线程的资源,另外也避开了布局和绘制两个子阶段,所以相对于重绘和重排,合成能大大提升绘制效率。

图示

image.png

触发条件

  • will-change
  • transform属性改变
  • 整个图层的几何变换,透明度变换,阴影。

优化方案

  1. 使用will-change提前声明,使得渲染引擎将该元素单独实现一帧。(空间换时间)。

⚠️:每当渲染引擎为一个元素准备一个独立层的时候,它占用的内存也会大大增加,因为从层树开始,后续每个阶段都会多一个层结构,这些都需要额外的内存,所以你需要恰当地使用 will-change。

 

参考:

极客时间:浏览器工作原理与实践

//blog.csdn.net/qq_42269433/article/details/81133772