重绘与回流

在网易一面的时候问到了重绘和回流,面试之前有看过,但是还是记得很模糊,所以特别写一篇来记一下。

Keyword: 重绘、回流

HTML渲染过程

浏览器在获取到了HTML之后,会把HTML解析成一个DOM树,HTML中的每一个标签都是这个树中的节点。这个树后续可以被JavaScript操作,包含了动态添加的节点,以及在页面中并不渲染的节点(display: none)。

在加载CSS的过程中,浏览器会对CSS进行解析,在其内部构成一个CSSOM(样式结构体)。在解析的过程中识别不了的样式会被浏览器自动忽略。

浏览器在渲染页面时,是利用DOM树和CSSOM进行综合计算,得到一个渲染树(render tree),然后再基于渲染树渲染最终的页面。

渲染树和DOM树很类似,但是二者的区别有很明显,渲染树的节点是包括各种样式的,而且不包括被隐藏了的节点。

特别:visibility: hidden设置后,元素仍然还在渲染树上,在布局上仍然会占用一定的空间。

之所以存在重绘和回流的区别,是因为一些操作可能只是非常小的改变渲染树的元素,并不影响布局,所以浏览器可以做更小消耗的操作。

重绘

重绘指渲染树的元素需要更新一些属性,这些属性仅影响元素的外观、风格,不影响到布局,此时页面会只进行重绘。比如设置background-color。

重绘是回流的一部分,发生了回流那么一定会发生重绘,这使得回流的资源消耗比重绘要大。

重绘对应的CSS属性:

  • color
  • border-style
  • border-radius
  • visibility
  • background
  • text-decoration
  • outline
  • box-shadow

回流

任何在渲染树中的元素,如果其尺寸、布局、隐藏等情况发生了变化,引起页面布局变化,导致渲染树需要重新构建的时候,这个情况下会进行回流。

引起回流的情况有:

  • 添加/删除可见的DOM
  • 元素的尺寸发生变化(包括边距、填充、边框)
  • 内容出现变化(比如输入了某个文本)
  • 浏览器窗口大小改变(resize)
  • 计算offsetWidth、offsetHeight
  • 改变了某个元素的style属性
  • 修改页面字体

需要注意的是,即使你改变的是一个父节点里面的子节点,但是受到影响,父节点也会触发一系列的回流。

由于回流消耗大,为了优化页面,我们需要尽可能减少回流。优化的方法有:

  • 利用documentFragment存储页面的一部分元素,在页面里对这一块DOM进行操作,最后再应用一系列的更改。它是在内存中的一块脱离文档树的内容,不会引起回流。
  • 不要频繁地更改style,可以用添加、删除class的方式规避一定的归流。
  • 尽可能避免使用table布局,table布局很容易发生回流
  • 不要频繁获取节点的属性值,比如scrollTop、clientWidth等。