重绘(Repaint) 和 重排(Reflow) 是浏览器在渲染网页时的两个重要过程,它们分别发生在元素的视觉外观或布局发生变化时。理解这两个概念有助于我们优化网页性能,避免不必要的计算,提升页面的响应速度和流畅度。
1. 重排(Reflow)
1.1 概念
重排(Reflow),又称为回流,是当页面的布局结构或几何属性(如宽度、高度、位置等)发生变化时,浏览器重新计算元素的尺寸和位置的过程。每次元素的尺寸、位置、或者页面的结构发生变化时,都会触发重排。重排是一个非常耗时的操作,尤其是在复杂的页面中,因为它会导致浏览器重新计算整个文档或者部分文档的布局。
1.2 触发重排的情况
以下操作都会触发重排:
- 添加或删除 DOM 元素。
- 改变元素的几何属性(如
width
、height
、margin
、padding
、border
等)。 - 移动元素(如改变
position
、top
、left
等)。 - 改变网页的布局属性(如
display
、float
、overflow
等)。 - 通过 JavaScript 获取某些属性(如
offsetWidth
、offsetHeight
、getBoundingClientRect()
等)。 - 改变浏览器窗口的大小(
resize
事件)。
1.3 重排的影响
由于重排涉及到页面布局的重新计算,所以它会对性能产生较大的影响。尤其是在有多个 DOM 节点依赖于某个父节点的布局时,重排可能会导致整个页面的重新布局,这将显著增加计算量,进而导致性能瓶颈。
示例:
const div = document.querySelector('div');
div.style.width = '300px'; // 触发重排
div.style.height = '200px'; // 触发重排
2. 重绘(Repaint)
2.1 概念
重绘(Repaint)发生在元素的外观(如颜色、背景色、边框等)发生变化,但布局没有改变时。重绘只涉及到元素的视觉更新,不涉及重新计算布局。因此,重绘的开销相对较小。
2.2 触发重绘的情况
以下操作会触发重绘:
- 改变元素的外观(如
color
、background-color
、visibility
、border-color
等),而不影响布局。 - 改变文字颜色、背景色、阴影等。
2.3 重绘的影响
虽然重绘的性能开销比重排要小很多,但频繁的重绘仍然会导致性能问题,特别是在高帧率动画中,频繁的视觉更新可能导致页面卡顿。
示例:
const div = document.querySelector('div');
div.style.backgroundColor = 'red'; // 触发重绘
div.style.color = 'blue'; // 触发重绘
3. 重排与重绘的区别
重排(Reflow) | 重绘(Repaint) |
---|---|
发生在页面的布局或几何属性(尺寸、位置)变化时 | 发生在元素的外观(颜色、背景色、阴影等)变化时 |
影响整个页面或部分页面的布局,需要重新计算 | 只影响视觉更新,不涉及布局计算 |
开销较大,尤其是在复杂页面中,性能消耗明显 | 开销较小,但频繁重绘也会影响性能 |
例子:添加 DOM 元素、改变元素尺寸 | 例子:改变元素颜色、背景色 |
4. 如何优化重排与重绘
过多的重排和重绘会显著影响网页的性能,特别是在页面包含大量 DOM 元素时。为了避免不必要的重排和重绘,以下是一些常见的优化策略:
4.1 减少重排
合并样式更改:避免逐条修改样式属性,使用
className
或cssText
一次性修改多个样式属性,减少多次重排。不推荐:
javascriptelement.style.width = '100px'; element.style.height = '200px';
推荐:
javascriptelement.style.cssText = 'width: 100px; height: 200px;';
批量修改 DOM:如果需要对多个元素进行修改,尽量一次性完成 DOM 的批量操作。可以使用
DocumentFragment
来批量操作多个 DOM 节点,再将它们一次性添加到文档中,减少重排次数。javascriptconst fragment = document.createDocumentFragment(); const newDiv = document.createElement('div'); fragment.appendChild(newDiv); document.body.appendChild(fragment); // 一次性添加,减少 DOM 操作
避免逐条读取会触发重排的属性:如
offsetHeight
、offsetWidth
、scrollTop
等。这些属性会强制浏览器刷新布局,如果在修改样式后立即读取这些属性,可能会导致强制重排。不推荐:
javascriptelement.style.width = '100px'; console.log(element.offsetHeight); // 读取 offsetHeight 触发强制重排
推荐:
javascriptconst height = element.offsetHeight; // 先读取 element.style.width = '100px'; // 再修改样式,减少强制重排
4.2 减少重绘
隐藏元素再操作:如果对某个元素有多次操作,可以先将该元素隐藏,等操作完成后再显示,避免多次重绘。
javascriptelement.style.display = 'none'; // 隐藏元素 // 执行一系列操作 element.style.display = ''; // 显示元素,重绘一次
使用
visibility
代替display: none
:使用visibility: hidden
不会触发重排,因为它不会影响布局,但display: none
会导致重排。
4.3 使用 requestAnimationFrame
优化动画
在进行频繁重绘的操作时,比如动画,可以使用 requestAnimationFrame
来优化页面的绘制。浏览器会在下一帧绘制时执行该回调,保证动画的流畅性,避免不必要的重绘。
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
top
、left
、bottom
、right
font-size
只会触发重绘的 CSS 属性:
color
background-color
visibility
border-color
box-shadow
text-decoration
总结
- 重排(Reflow):当页面的布局或几何属性发生变化时,浏览器重新计算布局的过程。重排开销较大,应该尽量避免频繁触发。
- 重绘(Repaint):当元素的外观(如颜色、背景等)发生变化时,浏览器重新绘制的过程。重绘的开销比重排小,但仍应避免频繁发生。
通过了解重排和重绘的触发条件和影响,前端开发者可以优化代码,减少不必要的计算开销,提升页面的渲染性能。