在浏览器渲染页面 的过程中,重排 (Reflow)和重绘(Repaint)是非常重要的概念,它们直接影响页面的性能,尤其是当页面包含大量元素或需要频繁更新时。
1. 重排(Reflow)
1.1 定义
重排是当页面的元素几何属性(如位置、大小、边距、布局等)发生变化时,浏览器需要重新计算这些元素的布局。
1.2 触发条件
重排通常发生在以下情况下:
- 改变元素的大小:例如修改元素的宽度、高度、边距、内边距等。
- 修改元素的位置:例如通过改变元素的 position 或 top、left、right、bottom 等属性。
- 显示或隐藏元素:例如通过 display: none 或 display: block。
- 添加或删除 DOM 元素:例如插入一个新的元素或移除一个已有元素。
- 改变 font-size、line-height 等可能影响页面布局的样式。
- 页面尺寸变化:例如窗口大小改变,或视口(viewport)尺寸改变时。
在触发重排后,浏览器会重新计算页面中每个元素的几何位置,重新渲染整个页面的布局。
2. 重绘(Repaint)
2.1 定义
重绘是当元素的外观(如颜色、背景、边框等)发生变化时,浏览器需要重新绘制元素的外观,但不会影响元素的布局或几何属性。
2.2 触发条件
重绘通常发生在以下情况下:
- 修改元素的背景颜色:例如通过修改 background-color。
- 修改元素的颜色:例如通过修改 color。
- 修改元素的边框样式:例如通过修改 border。
- 修改透明度:例如通过修改 opacity。
- 修改 visibility:例如通过修改 visibility: hidden。
只要元素的外观发生变化,就会触发重绘,但它不会涉及布局的计算,因此相对重排来说,重绘的代价较低。
3. 重排和重绘的区别
重排(Reflow):影响布局、位置、大小等几何信息,重新计算页面元素的几何属性,通常开销较大,影响页面性能。
重绘(Repaint):只影响外观样式(如颜色、边框),不涉及布局,开销较小。
重排比重绘更为昂贵,因为它涉及到重新计算和重新渲染元素的位置和大小,而重绘只涉及元素的外观。
4. 重排与重绘:是否一定影响性能?
重排和重绘它们的发生会消耗浏览器的计算资源,但并不是每次重排和重绘都会显著影响性能,具体取决于操作的频率、元素的数量、页面的复杂度以及浏览器的优化等。
4.1 重排是否一定影响性能?
重排通常比重绘更昂贵,因为它需要重新计算页面元素的几何位置、大小、布局等信息。虽然不一定每次重排都会带来显著的性能问题,但频繁发生时,尤其是在大规模、复杂布局的页面中,性能会受到明显影响。
- 少量重排:如果页面上的元素比较少,或者只是修改了一个小区域的样式,重排可能不会显著影响性能。浏览器通常会进行一些优化,避免完全重新计算布局。
- 频繁重排:如果页面频繁触发重排,比如不断地修改布局属性、位置、大小等,或者操作包含大量元素的 DOM 树时,重排的开销将变得更加显著,导致页面渲染性能下降,甚至出现卡顿。
例如,当频繁通过 JavaScript 修改元素的 width、height、position 等布局属性,或者动态添加/删除 DOM 元素时,会导致浏览器重新计算整个页面的布局,影响渲染效率。
4.2 重绘是否一定影响性能?
重绘 本质上是一个相对较轻的操作,因为它只涉及到重新绘制元素的外观(如颜色、背景、边框等),不会影响布局信息。然而,重绘的开销依然不能忽视,尤其是在页面元素多、更新频繁的情况下。
- 少量重绘:如果只修改了少数几个元素的样式,比如颜色或透明度等,且页面较简单,重绘对性能的影响一般不大。
- 频繁重绘:如果页面中存在大量元素,并且频繁地修改颜色、背景、透明度等外观样式,就会导致频繁的重绘,进而影响渲染效率。
尤其是在动画效果中,过多的 JavaScript 操控或 CSS 动画属性的修改(如颜色变换)会导致大量重绘,从而降低渲染速度,影响页面的流畅性。
4.3 什么时候重排和重绘对性能影响较小?
- 少量修改:如果只修改页面中少数几个元素的样式,并且这些修改不会导致整个页面的布局或几何属性发生变化,那么重排或重绘的影响几乎可以忽略不计。
- 浏览器优化:现代浏览器在渲染时通常会做一些优化,例如合并重排和重绘操作、推迟某些操作等,减少性能损耗。
4.4 如何判断是否影响性能?
重排和重绘的影响主要体现在页面渲染的响应时间 和流畅度上。可以通过以下方式判断性能是否受到影响:
- 页面卡顿:当页面的动画或交互变得不流畅、出现明显的延迟时,可能是频繁触发了重排或重绘。
- 浏览器开发者工具:使用浏览器的开发者工具(例如 Chrome 的 Performance 面板)来检查页面渲染过程,查看重排和重绘的次数及其对性能的影响。
- FPS(帧率)下降:如果页面的帧率明显下降(例如从 60 FPS 降到 30 FPS),这表明页面中可能存在过多的重排或重绘操作。
5. 如何避免?
5.1 避免频繁触发重排
1、批量更新样式
尽量避免在短时间内对同一元素的多个样式进行频繁的修改。可以通过一次性修改多个样式来减少重排的次数。
javascript
// 不推荐
element.style.width = '100px';
element.style.height = '100px';
element.style.margin = '10px';
// 每一行都会触发重排
// 推荐:批量修改
element.style.cssText = 'width: 100px; height: 100px; margin: 10px;';
// 一次性修改
2、避免修改布局属性
尽量避免改变会触发重排的属性,如 width、height、margin、padding、position 等。对于动态效果,尽量使用 transform 和 opacity 属性,这两个属性不会触发重排,只会触发重绘。
css
/* 使用 transform 和 opacity 而不是直接修改位置和大小 */
.element {
transform: translateX(100px); /* 不会触发重排 */
opacity: 0.5; /* 只会触发重绘 */
}
3、减少 DOM 操作
频繁地操作 DOM 会导致浏览器需要重新渲染页面。在一次操作中,尽量进行批量 DOM 更新。例如,可以将多个 DOM 更新放入 documentFragment 中,等操作完成后再一次性插入到文档中。
javascript
// 不推荐:频繁操作 DOM
for (let i = 0; i < 1000; i++) {
document.body.appendChild(createElement());
}
// 推荐:减少 DOM 操作
const fragment = document.createDocumentFragment();
for (let i = 0; i < 1000; i++) {
fragment.appendChild(createElement());
}
document.body.appendChild(fragment);
4、避免读取布局信息后修改布局
如果在修改元素布局之前读取了它的布局属性(如 offsetHeight,clientWidth 等),浏览器会强制进行重排。这是因为浏览器需要确保读取的布局信息是最新的。
javascript
// 不推荐:先读取布局信息再修改样式
const height = element.offsetHeight;
element.style.height = '200px'; // 触发重排和重绘
// 推荐:先修改样式再读取布局信息
element.style.height = '200px';
const height = element.offsetHeight;
5.2 避免频繁触发重绘
1、避免频繁修改颜色和背景色
每次修改颜色、背景色、透明度等会触发重绘。尽量避免不必要的频繁修改。
2、优化动画
CSS 动画(如 transition 和 keyframes)可以有效避免频繁的 JavaScript 操作。尽量通过 CSS 来控制动画,而非通过 JS 修改样式。