重绘与回流
当 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 之后,这个时候就不会影响其他元素也进行回流操作,而只会自己进行回流操作,那么这就是背后真正的原因。
1 | 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
常用方法生成独立图层:
1 | transform: translateZ(0); |
将频发触发重绘与回流的 Dom 元素,独立到一个独立的图层。但是 Composite Layers 合并图层也会消耗性能,过多的图层可能导致性能更差。
如何查看重绘与回流
1.按F12进入开发者工具,打开performance面板。
2.点击最左侧的黑色圆就可以开始一个新的监测记录。
案例说明
以天猫手机端为例,获取顶部的滚动栏渲染信息。
通过Main图表,Summary面板可以查看重绘与回流相关信息。
如何查看Render Layer
如下图所示:
Layers 面板可以查看详细的 Render Layer 信息。