回流和重绘

图片[1] - 回流和重绘 - 君霖驿站

浏览器解析HTML的简易过程

从上面这个图上,我们可以看到,浏览器渲染过程如下:

解析HTML,生成DOM树,解析CSS,生成CSSOM树

将DOM树和CSSOM树结合,生成渲染树(Render Tree)

有了渲染树,浏览器就知道了所有节点的样式,然后计算它们在页面上的大小和位置,最后把节点绘制到页面上。

更细致的如下图:

图片[2] - 回流和重绘 - 君霖驿站

由于浏览器使用流式布局,对Render Tree的计算通常只需要遍历一次就可以完成,但table及其内部元素除外,它们可能需要多次计算,通常要花3倍于同等元素的时间,这也是为什么要避免使用table布局的原因之一

回流(reflow)引起Dom树结构变化,改变页面布局。

重绘(repaint)不会引起Dom树变化及页面布局变化,只重新渲染页面样式

回流与重绘两者之间的联系在于: 触发回流一定会触发重绘,而触发重绘却不一定会触发回流。

我们可以把页面理解为一个黑板,黑板上有一朵画好的小花。现在我们要把这朵从左边(left)移到了右边(right),那我们是不是要先确定好右边的具体位置,画好形状(回流),再画上它原有的颜色(重绘)。

但如果我们仅仅是想换给花朵换一个颜色,那么只需擦掉花朵上的颜色,再重新涂上自己期望的颜色(重绘)就可以了。

回流(Reflow)

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

由于 reflow 是一种浏览器中的用户拦截(user-blocking)操作,所以了解不同的文档属性(DOM 层级(DOM depth),CSS 效率,不用类型的 style 变化)对 reflow 次数的影响,以及如何减少 reflow 次数,非常必要。(一个元素的回流可能会导致了其所有子元素以及DOM中紧随其后的节点、祖先节点元素的随后的回流。)

导致回流发生的一些因素:

浏览器窗口大小发生改变

元素字体大小变化

增加或者移除样式表

内容变化,比如用户在 input 框中输入文字, CSS3 动画,文字数量或者图片大小改变等

激活 CSS 伪类,比如 :hover

操作class属性

添加或删除可见的DOM元素

计算offsetWidth和offsetHeight属性

设置 style 属性的值

页面首次渲染

元素尺寸或者位置发生改变

一些常用且会导致回流的属性和方法:

offsetTop、offsetLeft、offsetWidth、offsetHeight
scrollTop、scrollLeft、scrollWidth、scrollHeight
clientTop、clientLeft、clientWidth、clientHeight
width、height
getComputedStyle()
getBoundingClientRect()

所以,我们应该避免频繁的使用上述的属性,他们都会强制渲染刷新队列。

重绘(Repaint)

重绘则是视觉效果变化引起的重新绘制。如改变visibility、outline、前景色、背景色、color等。

性能影响

回流比重绘的代价要更高,有时仅仅回流一个单一的元素,它的父元素以及任何跟随它的元素也会产生回流。

不过现代浏览器会对频繁的回流或重绘操作进行优化,浏览器会维护一个队列,把所有引起回流和重绘的操作放入到队列中,如果队列中的任务数量或者时间间隔达到一个阈值,浏览器会将队列清空,进行一次批处理,这样可以把多次回流和重绘变成一次。

如何减少回流和重绘

CSS

  • 1)使用 transform 替代 top;
  • 2)使用 visibility 替换 display: none ,因为前者只会引起重绘,后者会引发回流
  • 3)避免使用table布局,可能很小的一个小改动会造成整个 table 的重新布局。
  • 4)尽可能在DOM树的最末端改变class,回流是不可避免的,但可以减少其影响。尽可能在DOM树的最末端改变class,可以限制了回流的范围,使其影响尽可能少的节点。
  • 5)避免设置多层内联样式,CSS 选择符从右往左匹配查找,避免节点层级过多。
<div>
   <a> <span></span> </a>
</div>
<style>
   span {
      color: red;
    }
   div > a > span {
      color: red;
    }
</style>

对于第一种设置样式的方式来说,浏览器只需要找到页面中所有的 span 标签然后设置颜色,但是对于第二种设置样式的方式来说,浏览器首先需要找到所有的 span 标签,然后找到 span 标签上的 a 标签,最后再去找到 div 标签,然后给符合这种条件的 span 标签设置颜色,这样的递归过程就很复杂。所以我们应该尽可能的避免写过于具体的 CSS 选择器,然后对于 HTML 来说也尽量少的添加无意义标签,保证层级扁平。

  • 6)将动画效果应用到position属性为absolute或fixed的元素上,使它脱离文档流,避免影响其他元素的布局,这样只是一个重绘,而不是回流,同时,控制动画速度可以选择 requestAnimationFrame
  • 7)避免使用CSS表达式,可能会引发回流。
  • 8)将频繁重绘或者回流的节点设置为图层,图层能够阻止该节点的渲染行为影响别的节点,例如will-changevideoiframe等标签,浏览器会自动将该节点变为图层。
  • 9)CSS3 硬件加速(GPU加速),使用css3硬件加速,可以让transformopacityfiltersWill-change这些动画不会引起回流重绘 。但是对于动画的其它属性,比如background-color这些,还是会引起回流重绘的,不过它还是可以提升这些动画的性能。

JavaScript

  • 1、避免频繁操作样式,最好一次性重写style属性,或者将样式列表定义为class并一次性更改class属性。
  • 2、避免频繁操作DOM,创建一个documentFragment,在它上面应用所有DOM操作,最后再把它添加到文档中。
  • 3、也可以先为元素设置display: none,操作结束后再把它显示出来。因为在display属性为none的元素上进行的DOM操作不会引发回流和重绘。
  • 4、避免频繁读取会引发回流/重绘的属性,如果确实需要多次使用,就用一个变量缓存起来。
  • 5、对具有复杂动画的元素使用绝对定位,使它脱离文档流,否则会引起父元素及后续元素频繁回流。

© 版权声明
THE END
喜欢就支持一下吧
点赞0赞赏
分享
评论 抢沙发
jurieo的头像 - 君霖驿站

昵称

取消
昵称表情代码图片