回流和重绘属于浏览器渲染的机制,当渲染内容发生改变时,浏览器会进行回流或者重绘,以重新绘制。
重绘(Repaint) 和 回流(Reflow 或 Layout) 是浏览器渲染页面时的两个步骤:
- 重绘:当元素样式的改变不影响布局时,比如
color,background-color等,浏览器会将新样式应用到元素并重绘它。 - 回流:当元素的大小或者位置发生变化时,浏览器需要重新计算元素,然后进行渲染,这个过程就叫回流。例如改变
width,height,margin,display(以及其他一些相关属性)或者添加或删除元素都会触发回流。
回流是昂贵的操作,因为它涉及到部分或全部的 DOM 元素的重新布局和渲染。而重绘虽然开销不如回流大,但如果频繁重绘,也会有明显的性能问题。(这里可以追问:为什么频繁重绘,也会有明显的性能问题)
以下是一些优化重绘和回流的建议:
-
避免单个元素的复杂样式:一些复杂样式(如 CSS 阴影、渐变)可能会导致浏览器进行大量计算,从而引发重绘。使用时应保持适当的审慎。
-
批量修改 DOM:合并多个修改操作为一个操作,可以减少回流和重绘次数。例如,先将元素从 DOM 树上移除,进行修改后再添加回来,或者使用文档片段(DocumentFragment)创建一个子树,然后添加到 DOM。
-
缓存布局信息:如果需要多次读取元素的布局信息(如宽度、高度等),应该一次性读取并缓存结果。不然每次读取都可能导致回流。
-
利用 CSS3 动画:CSS3 动画的性能通常更好,因为浏览器可能会对其进行优化,比如在创建动画时,CSS3的属性会使用合成层(compositing layer)对元素单独处理,避免回流和重绘,使用加速的属性比如:transform。
-
使用
requestAnimationFrame:requestAnimationFrame可以使得修改操作在浏览器准备重绘时候执行,减少不必要的布局和渲染开销。 -
尽量分离读写布局信息的操作
注意:优化重绘和回流是为了提高用户界面的响应速度和流畅度。如果你的网页已经足够快,那么过多地优化可能会导致代码过于复杂,反而不值得。总是要在性能和代码可读性、可维护性之间找到平衡。
为什么频繁重绘,也会有明显的性能问题
在浏览器渲染一个Web页面时,会经历以下一系列过程:HTML解析成DOM,CSS解析成CSSOM(CSS Object Model),DOM和CSSOM合并成一个渲染树(Render Tree),然后根据渲染树来布局(Layout)和绘制(Paint)元素。
重绘(Repaint)就是这个过程的最后一步,也就是在屏幕上绘制元素的过程。当元素的外观、尺寸、颜色或者内容发生变化,就需要进行重绘。
然而,即使只是重绘,每次也都会涉及到浏览器的样式计算、布局和像素绘制等步骤。虽然它省去了重新计算布局的步骤,相较于回流(Reflow)来说,性能开销稍小。但是,如果频繁进行重绘,它仍然可能会消耗西方大量的 CPU 或 GPU 资源,导致页面卡顿,降低用户体验。
具体来说,重绘的性能问题主要表现在以下几个方面:
-
CPU和GPU开销:每次重绘都需要计算元素的样式,甚至可能需要进行布局,然后将元素绘制到屏幕上。即使浏览器优化,把部分工作交给 GPU 完成,这些操作也都需要消耗一定的CPU或GPU资源。
-
帧率下降:在进行动画或页面滚动等操作时,如果不能维持每秒60帧(60fps)的刷新率,用户就会感到明显的卡顿。如果每一帧都需要重绘一次,那么就需要在大约16毫秒内完成所有计算和绘制工作,否则帧率就会下降。
-
内存使用增加:在某些情况下,浏览器可能需要为重绘过程中的中间状态保存额外的图形数据,这将增加内存使用。
所以,对于那些需要频繁重绘的场景(比如动画),我们需要特别注意,并尽可能使用性能更高的方法(如使用 transform 和 opacity 属性进行硬件加速,或使用 canvas/WebGL 进行绘制等)。