# ❓介绍下重绘和重排(Repaint & Reflow),以及如何进行优化

这个题目牵扯到浏览器的渲染机制

# 浏览器的渲染机制

  1. 处理 HTML 标记并构建 DOM 树
  2. 处理 CSS 标记并构建 CSSOM 树
  3. 将 DOM 与 CSSOM 合并成一个渲染树
  4. 根据渲染树来布局,以计算每个节点的几何信息
  5. 将各个节点绘制到屏幕上

优化关键渲染路径就是指最大限度缩短执行上述第 1 步至第 5 步耗费的总时间。 这样一来,就能尽快将内容渲染到屏幕上,此外还能缩短首次渲染后屏幕刷新的时间,即为交互式内容实现更高的刷新率。

来自于MDN

# 重绘

由于节点的几何属性或者样式属性发生变化而不影响布局的,称为重绘。比如:outlinevisibilitycolorbackground-color等。同时浏览器也会重新验证其他节点的可见性。

# 重排(回流)

节点的几何属性发生变化,影响布局,称之为重排。重绘不一定重排,重排一定重绘。重排是影响浏览器性能的关键因素,因为其变化涉及到部分页面(或是整个页面)的布局更新。一个元素的重排可能会导致了其所有子元素以及 DOM 中紧随其后的节点、祖先节点元素的随后的重排。

# 那么什么操作会引发重排重绘呢

  • 添加或删除可见的 DOM 元素
  • 元素的位置发生变化
  • 元素的尺寸发生变化(包括外边距、内边框、边框大小、高度和宽度等)
  • 内容发生变化,比如文本变化或图片被另一个不同尺寸的图片所替代。
  • 页面一开始渲染的时候(这肯定避免不了)
  • 浏览器的窗口尺寸变化(因为重排是根据视口的大小来计算元素的位置和大小的)

# 浏览器优化机制

由于每次重排都会造成额外的计算消耗,因此大多数浏览器都会通过队列化修改并批量执行来优化重排过程。浏览器会将修改操作放入到队列里,直到过了一段时间或者操作达到了一个阈值,才清空队列。但是!当你获取布局信息的操作的时候,会强制队列刷新,比如当你访问以下属性或者使用以下方法:

offsetTopoffsetLeftoffsetWidthoffsetHeight

scrollTopscrollLeftscrollWidthscrollHeight

clientTopclientLeftclientWidthclientHeight

getComputedStyle() getBoundingClientRect

具体可以访问这个网站:https://gist.github.com/paulirish/5d52fb081b3570c81e3a

以上属性和方法都需要返回最新的布局信息,因此浏览器不得不清空队列,触发重排重绘来返回正确的值。因此,我们在修改样式的时候,**最好避免使用上面列出的属性,他们都会刷新渲染队列。**如果要使用它们,最好将值缓存起来

# 减少重排与重绘

  1. css

    • 使用 transform 替代 top
    • 使用 visibility 替换 display: none ,因为前者只会引起重绘,后者会引发重排
    • 避免使用 table 布局,table 及其内部元素除外,他们可能需要多次计算,通常要花 3 倍于同等元素的时间
    • 对具有复杂动画的元素使用绝对定位,使它脱离文档流,否则会引起父元素及后续元素频繁重排
    • css3 硬件加速(GPU 加速)可以让 transform、opacity、filters 这些动画不会引起重排重绘。对于动画的其它属性,比如 background-color 这些,还是会引起重排重绘的,不过它还是可以提升这些动画的性能。常见的触发硬件加速的 css 属性:transformopacityfiltersWill-change
  2. javascript

    • 合并相关样式操作,最好一次性重写 style 属性,或者将样式列表定义为 class 并一次性更改 class 属性。
    • 避免频繁的 DOM 操作,或者创建一个 documentFragment,在它上面应用所有 DOM 操作,最后再把它添加到文档中。
    • 避免频繁读取会引发重排/重绘的属性,如果确实需要多次使用,就用一个变量缓存起来。