# ❓介绍下重绘和重排(Repaint & Reflow),以及如何进行优化
这个题目牵扯到浏览器的渲染机制
# 浏览器的渲染机制
- 处理 HTML 标记并构建 DOM 树
- 处理 CSS 标记并构建 CSSOM 树
- 将 DOM 与 CSSOM 合并成一个渲染树
- 根据渲染树来布局,以计算每个节点的几何信息
- 将各个节点绘制到屏幕上
优化关键渲染路径就是指最大限度缩短执行上述第 1 步至第 5 步耗费的总时间。 这样一来,就能尽快将内容渲染到屏幕上,此外还能缩短首次渲染后屏幕刷新的时间,即为交互式内容实现更高的刷新率。
来自于MDN
# 重绘
由于节点的几何属性或者样式属性发生变化而不影响布局的,称为重绘。比如:outline
,visibility
,color
,background-color
等。同时浏览器也会重新验证其他节点的可见性。
# 重排(回流)
节点的几何属性发生变化,影响布局,称之为重排。重绘不一定重排,重排一定重绘。重排是影响浏览器性能的关键因素,因为其变化涉及到部分页面(或是整个页面)的布局更新。一个元素的重排可能会导致了其所有子元素以及 DOM 中紧随其后的节点、祖先节点元素的随后的重排。
# 那么什么操作会引发重排重绘呢
- 添加或删除可见的 DOM 元素
- 元素的位置发生变化
- 元素的尺寸发生变化(包括外边距、内边框、边框大小、高度和宽度等)
- 内容发生变化,比如文本变化或图片被另一个不同尺寸的图片所替代。
- 页面一开始渲染的时候(这肯定避免不了)
- 浏览器的窗口尺寸变化(因为重排是根据视口的大小来计算元素的位置和大小的)
# 浏览器优化机制
由于每次重排都会造成额外的计算消耗,因此大多数浏览器都会通过队列化修改并批量执行来优化重排过程。浏览器会将修改操作放入到队列里,直到过了一段时间或者操作达到了一个阈值,才清空队列。但是!当你获取布局信息的操作的时候,会强制队列刷新,比如当你访问以下属性或者使用以下方法:
offsetTop
、offsetLeft
、offsetWidth
、offsetHeight
scrollTop
、scrollLeft
、scrollWidth
、scrollHeight
clientTop
、clientLeft
、clientWidth
、clientHeight
getComputedStyle()
getBoundingClientRect
具体可以访问这个网站:https://gist.github.com/paulirish/5d52fb081b3570c81e3a
以上属性和方法都需要返回最新的布局信息,因此浏览器不得不清空队列,触发重排重绘来返回正确的值。因此,我们在修改样式的时候,**最好避免使用上面列出的属性,他们都会刷新渲染队列。**如果要使用它们,最好将值缓存起来
# 减少重排与重绘
css
- 使用
transform
替代top
- 使用
visibility
替换display: none
,因为前者只会引起重绘,后者会引发重排 - 避免使用
table
布局,table
及其内部元素除外,他们可能需要多次计算,通常要花 3 倍于同等元素的时间 - 对具有复杂动画的元素使用绝对定位,使它脱离文档流,否则会引起父元素及后续元素频繁重排
- css3 硬件加速(GPU 加速)可以让 transform、opacity、filters 这些动画不会引起重排重绘。对于动画的其它属性,比如 background-color 这些,还是会引起重排重绘的,不过它还是可以提升这些动画的性能。常见的触发硬件加速的 css 属性:
transform
,opacity
,filters
,Will-change
- 使用
javascript
- 合并相关样式操作,最好一次性重写 style 属性,或者将样式列表定义为 class 并一次性更改 class 属性。
- 避免频繁的 DOM 操作,或者创建一个 documentFragment,在它上面应用所有 DOM 操作,最后再把它添加到文档中。
- 避免频繁读取会引发重排/重绘的属性,如果确实需要多次使用,就用一个变量缓存起来。