浏览器的回流与重绘 (Reflow & Repaint)

写在前面

在讨论回流与重绘以前,咱们要知道:浏览器

  1. 浏览器使用流式布局模型 (Flow Based Layout)。
  2. 浏览器会把HTML解析成DOM,把CSS解析成CSSOMDOMCSSOM合并就产生了Render Tree
  3. 有了RenderTree,咱们就知道了全部节点的样式,而后计算他们在页面上的大小和位置,最后把节点绘制到页面上。
  4. 因为浏览器使用流式布局,对Render Tree的计算一般只须要遍历一次就能够完成,但table及其内部元素除外,他们可能须要屡次计算,一般要花3倍于同等元素的时间,这也是为何要避免使用table布局的缘由之一。

一句话:回流必将引发重绘,重绘不必定会引发回流。缓存

回流 (Reflow)

Render Tree中部分或所有元素的尺寸、结构、或某些属性发生改变时,浏览器从新渲染部分或所有文档的过程称为回流。布局

会致使回流的操做:性能

  • 页面首次渲染
  • 浏览器窗口大小发生改变
  • 元素尺寸或位置发生改变
  • 元素内容变化(文字数量或图片大小等等)
  • 元素字体大小变化
  • 添加或者删除可见DOM元素
  • 激活CSS伪类(例如::hover
  • 查询某些属性或调用某些方法

一些经常使用且会致使回流的属性和方法:字体

  • clientWidthclientHeightclientTopclientLeft
  • offsetWidthoffsetHeightoffsetTopoffsetLeft
  • scrollWidthscrollHeightscrollTopscrollLeft
  • scrollIntoView()scrollIntoViewIfNeeded()
  • getComputedStyle()
  • getBoundingClientRect()
  • scrollTo()

重绘 (Repaint)

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

性能影响

回流比重绘的代价要更高。动画

有时即便仅仅回流一个单一的元素,它的父元素以及任何跟随它的元素也会产生回流。code

现代浏览器会对频繁的回流或重绘操做进行优化:队列

浏览器会维护一个队列,把全部引发回流和重绘的操做放入队列中,若是队列中的任务数量或者时间间隔达到一个阈值的,浏览器就会将队列清空,进行一次批处理,这样能够把屡次回流和重绘变成一次。图片

当你访问如下属性或方法时,浏览器会马上清空队列:

  • clientWidthclientHeightclientTopclientLeft

  • offsetWidthoffsetHeightoffsetTopoffsetLeft

  • scrollWidthscrollHeightscrollTopscrollLeft

  • widthheight

  • getComputedStyle()

  • getBoundingClientRect()

由于队列中可能会有影响到这些属性或方法返回值的操做,即便你但愿获取的信息与队列中操做引起的改变无关,浏览器也会强行清空队列,确保你拿到的值是最精确的。

如何避免

CSS

  • 避免使用table布局。
  • 尽量在DOM树的最末端改变class
  • 避免设置多层内联样式。
  • 将动画效果应用到position属性为absolutefixed的元素上。
  • 避免使用CSS表达式(例如:calc())。

JavaScript

  • 避免频繁操做样式,最好一次性重写style属性,或者将样式列表定义为class并一次性更改class属性。
  • 避免频繁操做DOM,建立一个documentFragment,在它上面应用全部DOM操做,最后再把它添加到文档中。
  • 也能够先为元素设置display: none,操做结束后再把它显示出来。由于在display属性为none的元素上进行的DOM操做不会引起回流和重绘。
  • 避免频繁读取会引起回流/重绘的属性,若是确实须要屡次使用,就用一个变量缓存起来。
  • 对具备复杂动画的元素使用绝对定位,使它脱离文档流,不然会引发父元素及后续元素频繁回流。