重绘和重排(回流)

在了解重绘与重排之前,我们先来了解下浏览器的渲染过程,以便更好的理解重绘与重排。

浏览器的渲染过程

  1. 构建DOM树:渲染引擎解析HTML文档,首先将标签转换成DOM树中的DOM node(包括JS生成的标签),构建DOM树;
  2. 构建CSSOM树:解析对应的CSS样式文件(包括JS生成的样式和外部CSS样式),构建CSSOM树;
  3. 构建渲染树(Render Tree):CSSOM树构建结束后,和DOM树一起生成渲染树。渲染树中每个node都有自己的style,而且渲染树不包含隐藏的节点(比如display:none;),因为这些节点不会用于呈现;
  4. 布局渲染树(Layout/reflow):有了Render Tree后,从根节点递归调用,计算每一个元素的大小、位置等,给出每个节点所应该在屏幕上出现的精确坐标;
  5. 绘制渲染树(Painting/repaint):遍历渲染树,将各个节点绘制在屏幕上。


重绘和重排

当页面中元素样式的改变并不影响它在文档流中的位置时(例如:color、background-color、visibility等),浏览器会将新样式赋予给元素并重新绘制它,这个过程称为重绘。

当Render Tree中部分或全部元素的尺寸、结构、或某些属性发生改变时,浏览器重新渲染部分或全部文档的过程称为重排。

在网页生成的时候,至少会渲染一次。在用户访问过程中,还会不断的进行渲染。

下面三个操作会导致网页重新渲染:

  • 修改DOM
  • 修改样式表
  • 用户事件

重新进行渲染就需要重新生成布局重新绘制,前者就叫做重排(回流),后者叫做重绘

注意:重绘不一定会引发重排,但重排必定会引发重绘!


触发条件

触发重绘的条件:改变元素外观属性。例如:color、background-color等等。

触发重排的条件:

  1. 页面渲染初始化(这是无法避免的)
  2. 浏览器窗口尺寸改变(resize事件发生时)
  3. 元素位置的改变,使用动画
  4. 元素尺寸的改变:大小,外边距,边框
  5. 设置style属性
  6. 激活CSS伪类(例如::hover)
  7. 添加或删除可见的DOM元素
  8. 填充内容的改变,比如文本的改变或图片大小改变而引起的计算值宽度和高度的改变
  9. 查询或调用某些元素属性,比如:
    • offsetTop/offsetLeft/offsetWidth/offsetHeight
    • scrollTop/scrollLeft/scrollWidth/scrollHeight
    • clientTop/clientLeft/clientWidth/clientHeight
    • width/height
    • getComputedStyle()
    • currentStyle(IE)

注意:table元素的重排和重绘成本要高于div元素,因为table元素及其内部元素可能需要多次计算才能确定好其在渲染树中节点的属性值,比同等的其他元素要花很多时间,这就是尽量避免使用table布局页面的原因之一。


提高性能的技巧

  1. 由于浏览器会尽量把所有的变动都集中在一起,排成一个队列,然后一次性执行,尽量避免多次重新渲染。因此,在DOM的多个读操作(或多个写操作),应该放在一起。不要两个读操作之间,加入一个写操作。
  2. 如果某个样式是通过重排得到的,那么最好缓存结果,避免下一次用到的时候,浏览器又要重排。
  3. 不要一条条地改变样式,而要通过改变class,或者csstext属性,一次性地改变样式。
  4. 先将元素设为display: none(需要1次重排和重绘),然后对这个节点进行100次操作,最后再恢复显示(需要1次重排和重绘)。这样就只用两次重新渲染。
  5. position属性为absolute或fixed的元素,重排的开销会比较小,元素脱离了文档流,它的变化不会影响到其他元素;
  6. 尽量使用离线DOM,而不是真实的网面DOM,来改变元素样式。比如,操作Document Fragment对象,完成后再把这个对象加入DOM。再比如,使用 cloneNode() 方法,在克隆的节点上进行操作,然后再用克隆的节点替换原始节点。
  7. 只在必要的时候,才将元素的display属性为可见,因为不可见的元素不影响重排和重绘。另外,visibility : hidden的元素只对重绘有影响,不影响重排。
  8. 使用window.requestAnimationFrame()、window.requestIdleCallback()这两个方法调节重新渲染。

参考链接

网页性能管理详解 - 阮一峰

原文地址:https://www.cnblogs.com/LqZww/p/13595822.html