# 重绘与回流

Render Tree 当中有元素的大小、布局、结构等发生改变时,就会触发回流,每个页面在首次加载的时候都需要进行至少一次回流;

Render Tree 有元素属性需要更新,这些属性只是影响元素的外观、风格,那么则会触发重绘。

重绘不一定导致回流,回流一定会导致重绘。

回流比重绘的代价要更高。

# 触发重绘

  • color、background 相关属性(如:background-color、background-image 等)
  • outline 相关属性( outline-color、outline-width )、text-decoration
  • border-radius、visibility、box-shadow

# 触发回流

# 触发页面重布局

  • 盒子模型相关属性会触发重布局:width、height、padding、margin、display、border-width、border、min-height
  • 定位属性及浮动会触发重布局:top、bottom、left、right、position、float、clear
  • 改变节点内部文字结构也会触发重布局:text-align、font-weight、font-family、line-height、vertical-align、white-space、font-size、overflow、overflow-y

# 触发回流的操作

  • 页面初次渲染
  • 元素大小发生变化
  • 元素的位置发生变化
  • 浏览器窗口大小变化
  • 添加或者删除 DOM 元素
  • 激活 CSS 伪类
  • 改变字体
  • 获取即时计算的属性:offsetTop、offsetLeft、 offsetWidth、offsetHeight、scrollTop、scrollLeft、scrollWidth、scrollHeight、clientTop、clientLeft、clientWidth、clientHeight、getComputedStyle、scrollIntoView()、scrollIntoViewIfNeeded()getBoundingClientRect()、scrollTo()

# Flush 队列

现代浏览器会对频繁的回流或重绘操作进行优化:

浏览器会维护一个队列,把所有引起回流和重绘的操作放入队列中,如果队列中的任务数量或者时间间隔达到一个阈值的,浏览器就会将队列清空,进行一次批处理,这样可以把多次回流和重绘变成一次。

当你访问以下属性或方法时,浏览器会立刻清空队列:

  • clientWidth、clientHeight、clientTop、clientLeft
  • offsetWidth、offsetHeight、offsetTop、offsetLeft
  • scrollWidth、scrollHeight、scrollTop、scrollLeft
  • width、height
  • getComputedStyle()
  • getBoundingClientRect()

因为队列中可能会有影响到这些属性或方法返回值的操作,即使你希望获取的信息与队列中操作引发的改变无关,浏览器也会强行清空队列,确保你拿到的值是最精确的。

基本上都是获取即时计算的属性。

# 减少重绘与回流

# CSS

  • 避免使用table布局。
  • 尽可能在DOM树的最末端改变class。
  • 避免设置多层内联样式。
  • 将动画效果应用到position属性为absolute或fixed的元素上。
  • 避免使用CSS表达式(例如:calc())。

# JavaScript

  • 避免频繁操作样式,最好一次性重写style属性,或者将样式列表定义为class并一次性更改class属性。
  • 避免频繁操作DOM,创建一个documentFragment,在它上面应用所有DOM操作,最后再把它添加到文档中。
  • 也可以先为元素设置display: none,操作结束后再把它显示出来。因为在display属性为none的元素上进行的DOM操作不会引发回流和重绘。
  • 避免频繁读取会引发回流/重绘的属性,如果确实需要多次使用,就用一个变量缓存起来。
  • 对具有复杂动画的元素使用绝对定位,使它脱离文档流,否则会引起父元素及后续元素频繁回流。

# 扩展部分

更加完整的渲染流程

更加完整的渲染流程

上面提到把 position 设置为absolute 或者 fixed,其实就是把 Render Tree 上面的这个元素提升到了 Render Layer,提升到 Render Layer 之后,这个时候就不会影响其他元素也进行回流操作,而只会自己进行回流操作,那么这就是背后真正的原因。

Tips:动画除了可以使用 position 设置为 absolute 或 fixed 优化之外,还可以使用启用硬件加速( GPU加速 )来进行优化,比如说一些移动元素的操作我们可以使用 translate3d() 方法来完成,使用这个方法之后,我们就触发了硬件加速,由于硬件加速使用的是 GPU 绘制,所以速度更快,而且不会引起回流和重绘,性能也可以得到较大的提升。

# Chrome创建 Render Layer 图层的条件

  • 3D和透视变换CSS属性(perspective transform)
  • 使用加速视频解码的 video 标签
  • 3D Webgl的上下文或加速2D上下文的 canvas 节点
  • 混合插件 (如Flash)
  • css3硬件加速(GPU加速)transform,opacity,filters,Will-change

# 常用方法生成独立图层:

transform: translateZ(0);

will-change: transform;

将频发触发重绘与回流的 Dom 元素,独立到一个独立的图层。但是 Composite Layers 合并图层也会消耗性能,过多的图层可能导致性能更差。

# 如何查看重绘与回流

  • 1.按F12进入开发者工具,打开performance面板。

  • 2.点击最左侧的黑色圆就可以开始一个新的监测记录。

# 案例说明

以天猫手机端为例,获取顶部的滚动栏渲染信息。

渲染信息

通过Main图表,Summary面板可以查看重绘与回流相关信息。

重绘与回流信息

# 如何查看Render Layer

如下图所示:

Render Layer

Render Layer2

Layers 面板可以查看详细的 Render Layer 信息。

# 参考

更新时间: 5/13/2020, 11:23:00 PM