回流与重绘
几个核心要点
- 性能开销:回流 > 重绘
回流
回流也叫重排,是浏览器为了重新计算元素的布局而进行的操作。这是性能开销最大的操作。
以下情况会触发回流:
- 页面首次渲染
- 浏览器窗口大小发生改变
- 元素尺寸或位置发生改变
- 元素内容变化(文字数量或图片大小等)
- 元素字体大小变化
- 添加或删除可见的 DOM 元素
- 激活 CSS 伪类(例如::hover)
- 查询某些属性或调用某些方法:
- clientWidth、clientHeight、clientTop、clientLeft
- offsetWidth、offsetHeight、offsetTop、offsetLeft
- scrollWidth、scrollHeight、scrollTop、scrollLeft
- getComputedStyle()
- getBoundingClientRect()
重绘
重绘是浏览器为了重新绘制元素而进行的操作。以下情况会触发重绘:
- 元素外观改变,但不影响布局的属性改变,例如:
- color(颜色)
- background-color(背景色)
- visibility(可见性)
- outline(轮廓)
- border-radius(圆角)
- box-shadow(阴影)
- text-decoration(文本装饰)
- background-image(背景图片)
- background-position(背景位置)
- opacity(透明度)
性能优化建议
为了减少回流和重绘,提高页面性能,可以采取以下措施:
-
批量修改 DOM
// 使用文档片段
const fragment = document.createDocumentFragment();
for (let i = 0; i < 10; i++) {
const li = document.createElement("li");
li.innerHTML = `Item ${i}`;
fragment.appendChild(li);
}
document.getElementById("list").appendChild(fragment); -
使用 CSS 类统一修改样式
// 避免逐条修改样式
element.classList.add("new-styles"); -
避免频繁操作样式
// 不推荐
const element = document.getElementById("myElement");
element.style.width = "100px";
element.style.height = "100px";
element.style.margin = "10px";
// 推荐
element.style.cssText = "width: 100px; height: 100px; margin: 10px;"; -
对于复杂动画,使用绝对定位使其脱离文档流
.animated-element {
position: absolute;
/* 或者使用 fixed */
} -
使用 transform 替代 top/left 做位置移动
/* 不推荐 */
.element {
top: 10px;
left: 10px;
}
/* 推荐 */
.element {
transform: translate(10px, 10px);
} -
缓存布局信息
// 不推荐
for (let i = 0; i < 100; i++) {
element.style.left = element.offsetLeft + 1 + "px";
}
// 推荐
let left = element.offsetLeft;
for (let i = 0; i < 100; i++) {
left++;
element.style.left = left + "px";
} -
对于频繁重排的元素,使用 will-change 属性
.frequent-animation {
will-change: transform;
}
提示
will-change 属性是 CSS 的一个实验性属性,用于告诉浏览器某些属性可能会发生变化,从而提前进行优化。
通过合理运用这些优化技巧,可以显著减少页面的回流和重绘次数,提升页面性能和用户体验。