1.浏览器渲染机制
浏览器采用流式布局模型(Flow Based Layout)
浏览器会把HTML解析成DOM,把CSS解析成CSSOM,DOM和CSSOM合并就产生了渲染树(Render Tree)。
有了渲染树,浏览器就知道了所有节点的样式,然后计算它们在页面上的大小和位置,最后把节点绘制到页面上。过程如图(来源MDN)
由于浏览器使用流式布局,对Render Tree的计算通常只需要遍历一次就可以完成,但table及其内部元素除外,它们可能需要多次计算,通常要花3倍于同等元素的时间,这也是为什么要避免使用table布局的原因之一。
2. reflow(回流):布局或者几何属性改变,浏览器为了重新渲染部分或整个页面,重新计算页面元素位置和几何结构的操作。
由于 reflow 是一种浏览器中的用户拦截(user-blocking)操作,所以了解不同的文档属性(DOM 层级(DOM depth),CSS 效率,不用类型的 style 变化)对 reflow 次数的影响,以及如何减少 reflow 次数,非常必要。(一个元素的回流可能会导致了其所有子元素以及DOM中紧随其后的节点、祖先节点元素的随后的回流。)
3. repaint(重绘):是在一个元素的外观被改变,但没有改变布局的情况下发生,如改变visibility、outline、前景色、背景色、color等。
"According to Opera, repaint is expensive because the browser must verify the visibility of all other nodes in the DOM tree."
当repaint发生时,浏览器会验证DOM树上的所有其它结点的visibility属性,所以代价高昂。
reflow(回流)是导致DOM脚本执行低效的关键因素之一。页面上任何一个结点触发reflow,都会导致它的子结点及祖先结点重新渲染。回流必定会发生重绘,重绘不一定会引发回流。
在下列情况下会导致reflow发生:
- 改变窗囗大小
- 改变文字大小
- 添加/删除样式表
- 内容的改变,如用户在输入框中敲字(这样也会-_-||)
- 激活伪类,如:hover (IE里是一个兄弟结点的伪类被激活)
- 操作class属性
- 脚本操作DOM
- 计算offsetWidth和offsetHeight等
- 设置style属性
4. 浏览器优化
现代浏览器大多都是通过队列机制来批量更新布局,浏览器会把修改操作放在队列中,至少一个浏览器刷新(即16.6ms)才会清空队列,但当你获取布局信息的时候,队列中可能有会影响这些属性或方法返回值的操作,即使没有,浏览器也会强制清空队列,触发回流与重绘来确保返回正确的值。
主要包括以下属性或方法:
offsetTop、offsetLeft、offsetWidth、offsetHeight
scrollTop、scrollLeft、scrollWidth、scrollHeight
clientTop、clientLeft、clientWidth、clientHeight
width、height
getComputedStyle()
getBoundingClientRect()
所以,我们应该避免频繁的使用上述的属性,他们都会强制渲染刷新队列。
5. 减少重绘与回流
(一)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
,详见探讨 requestAnimationFrame。
7)避免使用CSS表达式,可能会引发回流。
8)将频繁重绘或者回流的节点设置为图层,图层能够阻止该节点的渲染行为影响别的节点,例如will-change
、video
、iframe
等标签,浏览器会自动将该节点变为图层。
9)CSS3 硬件加速(GPU加速),使用css3硬件加速,可以让transform
、opacity
、filters
、Will-change
这些动画不会引起回流重绘 。但是对于动画的其它属性,比如background-color
这些,还是会引起回流重绘的,不过它还是可以提升这些动画的性能。
(二)JS
1)避免频繁操作样式,最好一次性重写style属性,或者将样式列表定义为class并一次性更改class属性。
2)避免频繁操作DOM,创建一个documentFragment,在它上面应用所有DOM操作,最后再把它添加到文档中。
3)避免频繁读取会引发回流/重绘的属性,如果确实需要多次使用,就用一个变量缓存起来。
总结:
回流与重绘涉及浏览器的性能表现,涉及的知识点众多且零碎,记住主要的就行了。更细致的解释见你真的了解回流和重绘吗