Skip to content
快速预览

回流和重绘

✍️ w 🕒 2023-06-28 12:51:11(a year ago) 🔗 A.前端知识整理

回流(reflow)和重绘(repaint)是浏览器渲染过程中的两个重要概念。

回流,也称为重排,是指浏览器重新计算元素的大小和位置。当页面布局发生变化时,浏览器需要重新计算元素的布局。回流的触发条件包括:DOM结构发生改变(如添加或移除节点)、修改元素的布局属性(如宽度、高度、内边距等)、窗口尺寸发生变化(如窗口缩放)等。

图 1

从上图可以看出,如果你通过 JavaScript 或者 CSS 修改元素的几何位置属性,例如改变元素的宽度、高度等,那么浏览器会触发重新布局,解析之后的一系列子阶段,这个过程就叫重排。无疑,重排需要更新完整的渲染流水线,所以开销也是最大的

重绘,是指浏览器重新渲染页面的内容。当元素的视觉样式发生变化时,浏览器需要重新绘制这些元素。重绘的触发条件包括:修改元素的背景色、文字颜色、边框颜色等样式属性。

图 2

从图中可以看出,如果修改了元素的背景颜色,那么布局阶段将不会被执行,因为并没有引起几何位置的变换,所以就直接进入了绘制阶段,然后执行之后的一系列子阶段,这个过程就叫重绘。相较于重排操作,重绘省去了布局和分层阶段,所以执行效率会比重排操作要高一些。

需要注意的是,回流一定会引起重绘,因为元素的大小和位置发生变化后,浏览器需要重新绘制这些元素。但是,重绘不一定会引起回流。由于回流涉及到重新计算布局,因此它是一个性能消耗较大的过程。在进行页面优化时,应尽量减少回流和重绘的次数,以提高页面性能。

合成,更改一个既不要布局也不要绘制的属性,会发生什么变化呢?渲染引擎将跳过布局和绘制,只执行后续的合成操作,我们把这个过程叫做合成

图 3

在上图中,我们使用了 CSS 的 transform 来实现动画效果,这可以避开重排和重绘阶段,直接在非主线程上执行合成动画操作。这样的效率是最高的,因为是在非主线程上合成,并没有占用主线程的资源,另外也避开了布局和绘制两个子阶段,所以相对于重绘和重排,合成能大大提升绘制效率

避免回流

  1. 一次性修改样式:在修改元素样式时,尽量一次性完成,可以通过设置cssText属性或者添加class的方式来实现。这样可以避免多次修改样式导致的性能损耗。
js
// 改变dom高度和设置margin分开写,可能会出发两次重排 这样就一次了
box.style.cssText = "width: 200px; height: 200px; background-color: blue;";
  1. 避免频繁操作DOM:在对DOM元素进行操作时,尽量减少直接对页面上的元素进行操作。可以使用DocumentFragment或者父元素来完成需要的DOM操作,然后再一次性地将修改应用到页面上。这样可以减少页面的重绘和回流,提高性能。下面代码循环不停操作样式改变
js
function foo() {
    let time_li = document.getElementById("time_li")
    for (let i = 0; i < 100; i++) {
        let main_div = document.getElementById("mian_div")
        let new_node = document.createElement("li")
        let textnode = document.createTextNode("time.geekbang")
        new_node.appendChild(textnode);
        new_node.offsetHeight = time_li.offsetHeight;
        document.getElementById("mian_div").appendChild(new_node);
    }
}
  1. 使用position的absolute或者fixed:对于某些元素,可以使用position属性的absolute或者fixed值来进行定位。这样的元素在进行回流时,开销相对较小,不会对其他元素造成影响。这也有助于提高页面的性能。

参考

浏览器工作原理与实践_李兵

Released under the MIT License.