重绘与回流

回流

指改变几何属性的渲染

重绘

指改变外观属性而不影响几何属性的渲染

属性分类

  • 几何属性:包括布局、尺寸等可用数学几何衡量的属性
    • 布局:displayfloatpositionlisttableflexcolumnsgrid
    • 尺寸:marginpaddingborderwidthheight
  • 外观属性:包括界面、文字等可用状态向量描述的属性
    • 界面:appearanceoutlinebackgroundmaskbox-shadowbox-reflectfilteropacityclip
    • 文字:textfontword

PS:推荐一个属性渲染状态可视化的网站CssTriggers,可查看每个属性在渲染时产生的影响。

浏览器的渲染过程

![Snipaste_2020-10-03_17-43-05](D:\OneDrive - mail2.gdut.edu.cn\typora_img\重绘与回流\Snipaste_2020-10-03_17-43-05.png)

在页面加载时,浏览器渲染过程如下:

  1. 解析HTML,生成DOM树,解析CSS,生成CSSOM树
  2. 将DOM树和CSSOM树结合,生成渲染树(Render Tree)
  3. Layout(回流):根据生成的渲染树,进行回流(Layout),得到节点的几何信息(位置,大小)
  4. Painting(重绘):根据渲染树以及回流得到的几何信息,得到节点的绝对像素
  5. Display:将像素发送给GPU,展示在页面上。(这一步其实还有很多内容,比如会在GPU将多个合成层合并为同一个层,并展示在页面中。而css3硬件加速的原理则是新建合成层)

为了构建渲染树,浏览器主要完成了以下工作:

  1. 从DOM树的根节点开始遍历每个可见节点。
  2. 对于每个可见的节点,找到CSSOM树中对应的规则,并应用它们。
  3. 根据每个可见节点以及其对应的样式,组合生成渲染树。

渲染树中只会包含可见节点。不可见节点包括:

  • 一些不会渲染输出的节点,比如script、meta、link等。
  • 一些通过css进行隐藏的节点。如display:none。

注意,利用visibility和opacity隐藏的节点,还是会显示在渲染树上的。

由于浏览器使用流式布局,对Render Tree的计算通常只需要遍历一次就可以完成,但table及其内部元素除外,他们可能需要多次计算

可见,如果触发回流那么必然会触发重绘

浏览器如何处理

每句JS操作都去回流重绘的话,浏览器可能就会受不了。

浏览器会维护1个队列,把所有会引起回流、重绘的操作放入这个队列,等队列中的操作到了一定的数量或者到了一定的时间间隔,浏览器就会flush队列,进行一个批处理。这样就会让多次的回流、重绘变成一次回流重绘。

虽然有了浏览器的优化,但有时候我们写的一些代码可能会强制浏览器提前flush队列,这样浏览器的优化可能就起不到作用了。当你请求向浏览器请求一些 style信息的时候,就会让浏览器flush队列:

  1. offsetTop, offsetLeft, offsetWidth, offsetHeight
  2. scrollTop/Left/Width/Height
  3. clientTop/Left/Width/Height
  4. width,height
  5. 请求了getComputedStyle(), 或者 IE的 currentStyle

当你请求上面的一些属性的时候,浏览器为了给你最精确的值,需要flush队列,因为队列中可能会有影响到这些值的操作。即使你获取元素的布局和样式信息跟最近发生或改变的布局信息无关,浏览器都会强行刷新渲染队列。引擎会重新渲染来确保获取的值 是实时的

利用合成层性能优化:Composite

可以利用chrome开发者工具的Layers查看合成层

提升为合成层简单说来有以下几点好处:

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

提升合成层的最好方式是使用 CSS 的 will-change 属性。而 will-change 设置为 opacity、transform、top、left、bottom、right 可以将元素提升为合成层。

1
2
3
4
5
6
7
8
#target {
will-change: transform; //兼容性不好
}
//对于那些目前还不支持 will-change 属性的浏览器
//目前常用的是使用一个 3D transform 属性来强制提升为合成层
#target {
transform: translateZ(0);
}

层合成的过程会产生内存消耗,所以不要盲目开启层合成

参考

https://juejin.im/post/6844904161868251149
https://juejin.im/post/6844903859719143437#heading-1

作者

Liang

发布于

2020-10-04

更新于

2023-06-15

许可协议


评论