# 回流重绘
"重绘"不一定会出现"回流","回流"必然会引起"重绘"
# 回流
回流也叫重排
触发条件:
简单来说,就是当我们获取 DOM 属性值,或者对 DOM 结构的修改引发 DOM 几何尺寸变化的时候,会发生回流的过程。
具体一点,有以下的操作会触发回流:
- 页面初始渲染,这是开销最大的一次重排
增减
或者移动
可见的 DOM 元素- 改变元素位置,字体大小
- 改变元素尺寸,比如边距、填充、边框、宽度和高度等
- 改变元素内容,比如文字数量,图片大小等
- 改变浏览器窗口尺寸,比如
resize
事件发生时 - 读写
offset
、scroll
和client
属性,调用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
属性为absolute
或fixed
的元素上,这样对其他元素影响较小。
动画效果还应牺牲一些平滑,来换取速度,这中间的度自己衡量: 比如实现一个动画,以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 几何属性的变化,因此元素的位置信息不需要更新,从而省去布局的过程。流程如下:
跳过了生成布局树和建图层树的阶段,直接生成绘制列表,然后继续进行分块、生成位图等后面一系列操作。
可以看到,重绘不一定导致回流,但回流一定发生了重绘。