# 回流重绘

"重绘"不一定会出现"回流","回流"必然会引起"重绘"

# 回流

回流也叫重排

触发条件:

简单来说,就是当我们获取 DOM 属性值,或者对 DOM 结构的修改引发 DOM 几何尺寸变化的时候,会发生回流的过程。

具体一点,有以下的操作会触发回流:

  • 页面初始渲染,这是开销最大的一次重排
  • 增减或者移动可见的 DOM 元素
  • 改变元素位置,字体大小
  • 改变元素尺寸,比如边距、填充、边框、宽度和高度等
  • 改变元素内容,比如文字数量,图片大小等
  • 改变浏览器窗口尺寸,比如resize事件发生时
  • 读写 offsetscrollclient属性,调用 getComputedStyle 方法
  • 设置 style 属性的值,因为通过设置style属性改变结点样式的话,每一次设置都会触发一次reflow

回流过程

依照上面的渲染流水线,触发回流的时候,如果 DOM 结构发生改变,则重新渲染 DOM 树,然后将后面的流程(包括主线程之外的任务)全部走一遍。

# 回流优化

1. 样式集中改变

// bad
var left = 10;
var top = 10;
el.style.left = left + "px";
el.style.top  = top  + "px";
// good 
el.className += " theclassname";
// good
el.style.cssText += "; left: " + left + "px; top: " + top + "px;";

2. 分离读写操作

DOM 的多个读操作(或多个写操作),应该放在一起。不要两个读操作之间,加入一个写操作。

// bad 强制刷新 触发四次重排+重绘
div.style.left = div.offsetLeft + 1 + 'px';
div.style.top = div.offsetTop + 1 + 'px';
div.style.right = div.offsetRight + 1 + 'px';
div.style.bottom = div.offsetBottom + 1 + 'px';

// good 缓存布局信息 相当于读写分离 触发一次重排+重绘
var curLeft = div.offsetLeft;
var curTop = div.offsetTop;
var curRight = div.offsetRight;
var curBottom = div.offsetBottom;

div.style.left = curLeft + 1 + 'px';
div.style.top = curTop + 1 + 'px';
div.style.right = curRight + 1 + 'px';
div.style.bottom = curBottom + 1 + 'px';

原来的操作会导致四次重排,读写分离之后实际上只触发了一次重排,这都得益于浏览器的渲染队列机制:

当我们修改了元素的几何属性,导致浏览器触发重排或重绘时。它会把该操作放进渲染队列,等到队列中的操作到了一定的数量或者到了一定的时间间隔时,浏览器就会批量执行这些操作。

3. 将 DOM 离线

  • 使用 display:none

    • 一旦我们给元素设置 display:none 时(只有一次重排重绘),元素便不会再存在在渲染树中,相当于将其从页面上“拿掉”,我们之后的操作将不会触发重排和重绘,添加足够多的变更后,通过 display 属性显示(另一次重排重绘)。通过这种方式即使大量变更也只触发两次重排。
    • 另外,visibility : hidden 的元素只对重绘有影响,不影响重排。
  • 通过使用 DocumentFragment (opens new window) 创建一个dom碎片,在它上面批量操作dom,操作完成之后,再添加到文档中,这样只会触发一次重排。

  • 复制节点,在副本上工作,然后替换它!

4.使用 absolute 或 fixed 脱离文档流

使用绝对定位会使的该元素单独成为渲染树中 body 的一个子元素,重排开销比较小,不会对其它节点造成太多影响。当你在这些节点上放置这个元素时,一些其它在这个区域内的节点可能需要重绘,但是不需要重排。

5.优化动画

  • 可以把动画效果应用到 position 属性为 absolutefixed 的元素上,这样对其他元素影响较小。

动画效果还应牺牲一些平滑,来换取速度,这中间的度自己衡量: 比如实现一个动画,以1个像素为单位移动这样最平滑,但是Layout就会过于频繁,大量消耗CPU资源,如果以3个像素为单位移动则会好很多

  • 启用GPU加速 GPU 硬件加速是指应用 GPU 的图形性能对浏览器中的一些图形操作交给 GPU 来完成,因为 GPU 是专门为处理图形而设计,所以它在速度和能耗上更有效率。

GPU 加速通常包括以下几个部分:Canvas2D,布局合成, CSS3转换(transitions),CSS3 3D变换(transforms),WebGL和视频(video)。

 /*
  * 根据上面的结论
  * 将 2d transform 换成 3d
  * 就可以强制开启 GPU 加速
  * 提高动画性能
  */
div {
    transform: translate3d(10px, 10px, 0);
}

# 重绘

当一个元素的外观发生改变,但没有改变布局,重新把元素外观绘制出来的过程,叫做重绘。

常见的引起重绘的属性:

属性
color text-decoration outline-color outline-width
border-style background-image outline box-shadow
visibility background-position outline-style background-size
background background-repeat border-radius

重绘过程

由于没有导致 DOM 几何属性的变化,因此元素的位置信息不需要更新,从而省去布局的过程。流程如下:

跳过了生成布局树和建图层树的阶段,直接生成绘制列表,然后继续进行分块、生成位图等后面一系列操作。

可以看到,重绘不一定导致回流,但回流一定发生了重绘。