目录

1. 更新了元素的几何属性(重排)

2. 更新元素的绘制属性(重绘)

3. 直接合成阶段

如何减少重排与重绘


1. 更新了元素的几何属性(重排)

六、“重排”“重绘”和“合成”
更新元素的几何属性

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

2. 更新元素的绘制属性(重绘)

接下来,我们再来看看重绘,比如通过 JavaScript 更改某些元素的背景颜色,渲染流水线会怎样调整呢?你可以参考下图:

六、“重排”“重绘”和“合成”
更新元素背景

从图中可以看出,如果修改了元素的背景颜色,那么布局阶段将不会被执行,因为并没有引起几何位置的变换,所以就直接进入了绘制阶段,然后执行之后的一系列子阶段,这个过程就叫重绘。相较于重排操作,重绘省去了布局和分层阶段,所以执行效率会比重排操作要高一些。

3. 直接合成阶段

那如果你更改一个既不要布局也不要绘制的属性,会发生什么变化呢?渲染引擎将跳过布局和绘制,只执行后续的合成操作,我们把这个过程叫做合成。具体流程参考下图:

六、“重排”“重绘”和“合成”
避开重排和重绘

在上图中,我们使用了 CSS 的 transform 来实现动画效果,这可以避开重排和重绘阶段,直接在非主线程上执行合成动画操作。这样的效率是最高的,因为是在非主线程上合成,并没有占用主线程的资源,另外也避开了布局和绘制两个子阶段,所以相对于重绘和重排,合成能大大提升绘制效率。

如何减少重排与重绘

  • 使用 class 操作样式,而不是频繁操作内联style
    其实也就是样式集中管理,多个元素使用同一style,都需要改变的话就需要触发多次重绘或重排,使用class一次搞定。

  • 避免使用 table 布局
    table及其内部元素可能需要多次计算才能确定好其在渲染树中节点的属性值,比同等元素要多花两倍时间,这就是我们尽量避免使用table布局页面的原因之一。

  • 批量dom 操作,例如 createDocumentFragment,或者使用框架,例如 React
    也就是虚拟节点,避免全部元素重排,只是对改变的节点更新

  • Debounce window resize 事件(防抖)

  • 对 dom 属性的读写要分离
    写:
    div.style.left = ‘10px’;
    div.style.top = ‘10px’;
    div.style.width = ‘20px’;
    div.style.height = ‘20px’;
    读:
    console.log(div.offsetLeft); // 会进行一次重排
    console.log(div.offsetTop);
    console.log(div.offsetWidth);
    console.log(div.offsetHeight);
    样式的设置是放入渲染队列中的,要获取样式属性值,会直接将渲染队列flush。像这样读写分离的话,只执行了一次重排。
    如若:
    div.style.left = ‘10px’;
    console.log(div.offsetLeft);
    div.style.top = ‘10px’;
    console.log(div.offsetTop);
    执行了两次次重排。

  • will-change: transform 做优化 (CSS 硬件加速)

  • 将需要多次重排的元素(需要动画表现的元素),position属性设为absolute或fixed,元素脱离了文档流,它的变化不会影响到其他元素,降低重排的代价;

  • 使用 display:none ,不使用 visibility,也不要改变 它的 z-index
    其实就是减少重排或重绘的内容

相关文章: