一、重绘和回流的定义
- 回流(Reflow):也称为布局(Layout),当页面中的元素的尺寸、结构或者位置发生变化时,浏览器需要重新计算元素的几何属性,这个过程称为回流。例如,当窗口大小改变、元素的尺寸改变、添加或删除元素等情况发生时,都会触发回流。
- 重绘(Repaint):当页面中的元素的外观(如颜色、背景色、边框等)发生改变,但不影响元素的布局时,浏览器会重新绘制这个元素,这个过程称为重绘。
二、触发回流和重绘的情况
回流的触发情况:
- 页面首次加载。
- 浏览器窗口大小改变。
- 元素的尺寸、位置发生改变(如通过 JavaScript 改变元素的宽度、高度、边距等)。
- 元素内容发生变化(如文本内容改变或图片被替换)。
- 添加或删除可见的 DOM 元素。
- 激活 CSS 伪类(如
:hover
等)。
重绘的触发情况:
- 元素的颜色、背景色、边框颜色等外观属性发生改变。
- 文本内容的字体颜色、大小等属性发生改变。
三、优化重绘和回流的方法
避免频繁操作样式
- 尽量减少通过 JavaScript 频繁地操作元素的样式。可以将多个样式的修改合并到一起,然后一次性应用到元素上。例如:
```javascript
const element = document.getElementById('myElement');
// 不好的做法
element.style.backgroundColor = 'red';
element.style.border = '1px solid black';
element.style.marginTop = '10px';
// 好的做法
element.className = 'newStyle';`` 在好的做法中,通过修改元素的
className`属性,将多个样式的修改合并到一个 CSS 类中,减少了对元素样式的频繁操作,从而减少回流和重绘的次数。- 尽量减少通过 JavaScript 频繁地操作元素的样式。可以将多个样式的修改合并到一起,然后一次性应用到元素上。例如:
避免频繁读取和修改 DOM 属性
- 频繁地读取和修改 DOM 属性会导致回流和重绘。如果需要读取和修改 DOM 属性,可以先将其存储在变量中,然后在合适的时候一次性应用。例如:
```javascript
const element = document.getElementById('myElement');
// 不好的做法
for (let i = 0; i < 100; i++) {
element.style.top = element.offsetTop + 1 + 'px';
}
// 好的做法
let top = element.offsetTop;
for (let i = 0; i < 100; i++) {
top++;
}
element.style.top = top + 'px';`` 在好的做法中,先将元素的
offsetTop属性存储在变量
top中,然后在循环中修改这个变量,最后一次性将修改后的
top值应用到元素的
style.top`属性上,减少了对 DOM 属性的频繁读取和修改,从而减少回流和重绘的次数。- 频繁地读取和修改 DOM 属性会导致回流和重绘。如果需要读取和修改 DOM 属性,可以先将其存储在变量中,然后在合适的时候一次性应用。例如:
使用 CSS 动画代替 JavaScript 动画
- CSS 动画通常比 JavaScript 动画更高效,因为浏览器可以对 CSS 动画进行优化,减少回流和重绘的次数。例如:
```css
.animated-element {
animation: myAnimation 2s ease-in-out;
}
@keyframes myAnimation {
from {transform: translateX(0);
}
to {transform: translateX(100px);
}
}
```
在上述代码中,使用 CSS 动画将元素从左侧平移到右侧,浏览器可以对这个动画进行优化,减少回流和重绘的次数。- CSS 动画通常比 JavaScript 动画更高效,因为浏览器可以对 CSS 动画进行优化,减少回流和重绘的次数。例如:
使用文档碎片(DocumentFragment)
- 当需要添加大量的 DOM 元素时,可以先将这些元素添加到文档碎片中,然后一次性将文档碎片添加到页面中,这样可以减少回流和重绘的次数。例如:
在上述代码中,先创建一个文档碎片,然后在循环中创建 100 个const fragment = document.createDocumentFragment(); for (let i = 0; i < 100; i++) { const element = document.createElement('div'); element.textContent = 'New Element'; fragment.appendChild(element); } document.getElementById('container').appendChild(fragment);
div
元素并添加到文档碎片中,最后将文档碎片一次性添加到页面中的container
元素中,减少了回流和重绘的次数。
- 当需要添加大量的 DOM 元素时,可以先将这些元素添加到文档碎片中,然后一次性将文档碎片添加到页面中,这样可以减少回流和重绘的次数。例如:
避免使用 table 布局
table
布局的回流和重绘成本比较高,因为浏览器需要对整个表格进行重新布局。如果可能的话,尽量使用div
和CSS
布局代替table
布局。
对频繁操作的元素进行绝对定位或固定定位
- 对频繁操作的元素进行绝对定位或固定定位,可以将其从文档流中移除,这样对其进行操作时不会影响其他元素的布局,从而减少回流的次数。但是需要注意,绝对定位或固定定位可能会影响页面的整体布局,需要谨慎使用。
使用虚拟 DOM
- 虚拟 DOM 是一种在内存中模拟 DOM 树的技术,可以减少对真实 DOM 的操作次数,从而减少回流和重绘的次数。例如,React 和 Vue.js 等框架都使用了虚拟 DOM 技术。