重排和重绘

在浏览器中,重排(Reflow)重绘(Repaint)是渲染引擎的两个重要过程。它们主要用于响应 DOM 和 CSS 的更改,但触发条件和性能消耗不同:


一、重排(Reflow)

定义

重排指的是当页面的布局或几何属性发生变化时,浏览器需要重新计算元素的位置尺寸,并重新构建渲染树。

触发重排的操作

重排通常由以下操作触发:

添加、删除或修改 DOM 元素

  • 动态插入新节点或移除已有节点。
  • const div = document.createElement('div'); document.body.appendChild(div); // 触发重排

修改元素的几何属性

  • widthheightpaddingmarginbordertopleftfont-size 等。
  • element.style.width = '200px'; // 触发重排

修改文档流

  • 如更改 displaypositionfloat 等。
  • element.style.display = 'block'; // 触发重排

读取影响布局的属性

  • offsetWidthoffsetHeightclientWidthgetBoundingClientRect() 等,这会强制触发重排以保证读取到的是最新的布局信息
  • console.log(element.offsetWidth); // 触发重排

浏览器窗口大小发生变化

  • 浏览器窗口调整时会重新计算布局。

性能消耗

重排是一个性能开销较大的操作,因为它会导致整个页面或部分页面的重新计算,尤其是在涉及复杂布局或大量 DOM 节点时。


二、重绘(Repaint)

定义

重绘指的是当页面的视觉样式发生变化但布局没有变化时,浏览器会重新绘制元素的外观,而无需重新计算位置或尺寸。

触发重绘的操作

重绘通常由以下操作触发:

修改元素的外观属性

  • colorbackground-colorvisibilityborder-colorbox-shadow 等。
  • element.style.backgroundColor = 'red'; // 触发重绘

修改透明度或显示状态

  • opacityvisibility(非 display: none)。
  • element.style.opacity = '0.5'; // 触发重绘

性能消耗

重绘只涉及图层的重新绘制,通常比重排开销小,因为它不涉及布局计算和文档流的重新生成。


三、重排和重绘的区别

类型触发条件性能开销
重排几何属性和布局变化(宽高、位置、文档流)
重绘视觉样式变化(颜色、背景、透明度)较低

四、如何减少重排和重绘

减少重排

批量修改样式

  • 避免多次修改样式属性,可以通过 classNamecssText 一次性修改。element.style.cssText = "width: 100px; height: 100px;";

离线操作 DOM

  • 使用 DocumentFragment 或将元素设置为 display: none 后再操作,最后统一插入或显示。
  • const fragment = document.createDocumentFragment(); for (let i = 0; i < 100; i++) { const div = document.createElement('div'); fragment.appendChild(div); } document.body.appendChild(fragment); // 只重排一次

避免触发强制同步布局

  • 避免频繁读取和写入布局信息,尽量将读取和写入操作分开。
  • const width = element.offsetWidth; // 读取布局信息element.style.width = width + '10px'; // 写入新样式

使用 transformopacity

  • 对需要移动或动画的元素,尽量使用 transformopacity,避免直接操作 topleft 等布局属性。

减少重绘

减少复杂的视觉效果

  • 避免使用过多的阴影、渐变等效果。

避免过度的样式修改

  • 避免频繁触发非必要的样式变化。

合并绘制区域

  • 尽量将需要改变样式的多个元素合并到一个容器中,减少重绘的次数。

五、重排与重绘的常见优化方式:分层

通过GPU 加速(CSS will-changetransform)将频繁操作的部分单独分层,可以减少重排和重绘对主渲染线程的影响。

示例:通过 GPU 加速优化

.element { will-change: transform, opacity; /* 告诉浏览器提前优化 */}

// 动画操作只影响 transform 和 opacity,不会触发重排element.style.transform = 'translateX(100px)'; element.style.opacity = '0.5';