Skip to content

重绘(Repaint)重排(Reflow) 是浏览器在渲染网页时的两个重要过程,它们分别发生在元素的视觉外观或布局发生变化时。理解这两个概念有助于我们优化网页性能,避免不必要的计算,提升页面的响应速度和流畅度。

1. 重排(Reflow)

1.1 概念

重排(Reflow),又称为回流,是当页面的布局结构或几何属性(如宽度、高度、位置等)发生变化时,浏览器重新计算元素的尺寸和位置的过程。每次元素的尺寸、位置、或者页面的结构发生变化时,都会触发重排。重排是一个非常耗时的操作,尤其是在复杂的页面中,因为它会导致浏览器重新计算整个文档或者部分文档的布局。

1.2 触发重排的情况

以下操作都会触发重排:

  • 添加或删除 DOM 元素。
  • 改变元素的几何属性(如 widthheightmarginpaddingborder 等)。
  • 移动元素(如改变 positiontopleft 等)。
  • 改变网页的布局属性(如 displayfloatoverflow 等)。
  • 通过 JavaScript 获取某些属性(如 offsetWidthoffsetHeightgetBoundingClientRect() 等)。
  • 改变浏览器窗口的大小(resize 事件)。

1.3 重排的影响

由于重排涉及到页面布局的重新计算,所以它会对性能产生较大的影响。尤其是在有多个 DOM 节点依赖于某个父节点的布局时,重排可能会导致整个页面的重新布局,这将显著增加计算量,进而导致性能瓶颈。

示例

javascript
const div = document.querySelector('div');
div.style.width = '300px';  // 触发重排
div.style.height = '200px'; // 触发重排

2. 重绘(Repaint)

2.1 概念

重绘(Repaint)发生在元素的外观(如颜色、背景色、边框等)发生变化,但布局没有改变时。重绘只涉及到元素的视觉更新,不涉及重新计算布局。因此,重绘的开销相对较小。

2.2 触发重绘的情况

以下操作会触发重绘:

  • 改变元素的外观(如 colorbackground-colorvisibilityborder-color 等),而不影响布局。
  • 改变文字颜色、背景色、阴影等。

2.3 重绘的影响

虽然重绘的性能开销比重排要小很多,但频繁的重绘仍然会导致性能问题,特别是在高帧率动画中,频繁的视觉更新可能导致页面卡顿。

示例

javascript
const div = document.querySelector('div');
div.style.backgroundColor = 'red'; // 触发重绘
div.style.color = 'blue';          // 触发重绘

3. 重排与重绘的区别

重排(Reflow)重绘(Repaint)
发生在页面的布局或几何属性(尺寸、位置)变化时发生在元素的外观(颜色、背景色、阴影等)变化时
影响整个页面或部分页面的布局,需要重新计算只影响视觉更新,不涉及布局计算
开销较大,尤其是在复杂页面中,性能消耗明显开销较小,但频繁重绘也会影响性能
例子:添加 DOM 元素、改变元素尺寸例子:改变元素颜色、背景色

4. 如何优化重排与重绘

过多的重排和重绘会显著影响网页的性能,特别是在页面包含大量 DOM 元素时。为了避免不必要的重排和重绘,以下是一些常见的优化策略:

4.1 减少重排

  • 合并样式更改:避免逐条修改样式属性,使用 classNamecssText 一次性修改多个样式属性,减少多次重排。

    不推荐

    javascript
    element.style.width = '100px';
    element.style.height = '200px';

    推荐

    javascript
    element.style.cssText = 'width: 100px; height: 200px;';
  • 批量修改 DOM:如果需要对多个元素进行修改,尽量一次性完成 DOM 的批量操作。可以使用 DocumentFragment 来批量操作多个 DOM 节点,再将它们一次性添加到文档中,减少重排次数。

    javascript
    const fragment = document.createDocumentFragment();
    const newDiv = document.createElement('div');
    fragment.appendChild(newDiv);
    document.body.appendChild(fragment);  // 一次性添加,减少 DOM 操作
  • 避免逐条读取会触发重排的属性:如 offsetHeightoffsetWidthscrollTop 等。这些属性会强制浏览器刷新布局,如果在修改样式后立即读取这些属性,可能会导致强制重排。

    不推荐

    javascript
    element.style.width = '100px';
    console.log(element.offsetHeight);  // 读取 offsetHeight 触发强制重排

    推荐

    javascript
    const height = element.offsetHeight; // 先读取
    element.style.width = '100px';       // 再修改样式,减少强制重排

4.2 减少重绘

  • 隐藏元素再操作:如果对某个元素有多次操作,可以先将该元素隐藏,等操作完成后再显示,避免多次重绘。

    javascript
    element.style.display = 'none';  // 隐藏元素
    // 执行一系列操作
    element.style.display = '';      // 显示元素,重绘一次
  • 使用 visibility 代替 display: none:使用 visibility: hidden 不会触发重排,因为它不会影响布局,但 display: none 会导致重排。

4.3 使用 requestAnimationFrame 优化动画

在进行频繁重绘的操作时,比如动画,可以使用 requestAnimationFrame 来优化页面的绘制。浏览器会在下一帧绘制时执行该回调,保证动画的流畅性,避免不必要的重绘。

javascript
function animate() {
  element.style.left = `${newPosition}px`;
  requestAnimationFrame(animate);
}
requestAnimationFrame(animate);

4.4 避免频繁的 DOM 操作

尽量减少对 DOM 的频繁操作,尤其是涉及到布局变化的操作。使用虚拟 DOM 或缓存的方式来减少对真实 DOM 的操作次数。

5. CSS 属性对重排与重绘的影响

一些 CSS 属性会触发重排,而另一些则只会触发重绘。了解这些属性的影响可以帮助开发者减少性能开销。

  • 会触发重排的 CSS 属性

    • width
    • height
    • margin
    • padding
    • display
    • border
    • position
    • topleftbottomright
    • font-size
  • 只会触发重绘的 CSS 属性

    • color
    • background-color
    • visibility
    • border-color
    • box-shadow
    • text-decoration

总结

  • 重排(Reflow):当页面的布局或几何属性发生变化时,浏览器重新计算布局的过程。重排开销较大,应该尽量避免频繁触发。
  • 重绘(Repaint):当元素的外观(如颜色、背景等)发生变化时,浏览器重新绘制的过程。重绘的开销比重排小,但仍应避免频繁发生。

通过了解重排和重绘的触发条件和影响,前端开发者可以优化代码,减少不必要的计算开销,提升页面的渲染性能。