回流也叫重排,是指当DOM元素的布局属性发生变化时,浏览器需要重新计算元素的位置和尺寸,然后重新构建渲染树的过程。重绘则是指元素的外观属性发生变化,但不影响布局时,浏览器重新绘制元素的过程。回流一定会触发重绘,而重绘不一定会触发回流,减少回流是HTML性能优化的核心方向之一。

减少回流的核心方法
1. 避免频繁操作DOM样式
频繁修改单个元素的样式会多次触发回流,建议将多次样式修改合并为一次操作。比如可以先修改元素的class类名,再一次性应用所有样式,而不是逐行修改style属性。
<!-- 不推荐的写法 -->
<div id="box"></div>
<script>
const box = document.getElementById('box');
box.style.width = '100px';
box.style.height = '100px';
box.style.backgroundColor = 'red';
</script>
<!-- 推荐的写法 -->
<style>
.box-style {
width: 100px;
height: 100px;
background-color: red;
}
</style>
<div id="box"></div>
<script>
const box = document.getElementById('box');
box.className = 'box-style';
</script>
2. 批量修改DOM时使用文档片段
如果需要多次创建或插入DOM元素,直接使用appendChild会每次触发回流,使用DocumentFragment可以先在内存中完成所有DOM操作,再一次性插入到页面中,减少回流次数。
// 不推荐的写法
const list = document.getElementById('list');
for (let i = 0; i < 10; i++) {
const li = document.createElement('li');
li.textContent = `列表项${i}`;
list.appendChild(li); // 每次插入都会触发回流
}
// 推荐的写法
const list = document.getElementById('list');
const fragment = document.createDocumentFragment();
for (let i = 0; i < 10; i++) {
const li = document.createElement('li');
li.textContent = `列表项${i}`;
fragment.appendChild(li);
}
list.appendChild(fragment); // 仅触发一次回流
3. 避免强制同步布局
部分DOM属性(如offsetWidth、offsetHeight、clientWidth等)的读取会强制浏览器提前执行布局计算,也就是强制同步布局,这会打断浏览器的优化流程,增加回流概率。建议先读取所有需要的布局属性,再统一修改样式。
// 不推荐的写法:读写交替触发多次回流
const box = document.getElementById('box');
box.style.width = '200px';
console.log(box.offsetWidth); // 触发回流
box.style.height = '200px';
console.log(box.offsetHeight); // 触发回流
// 推荐的写法:先读后写,减少回流
const box = document.getElementById('box');
const width = box.offsetWidth;
const height = box.offsetHeight;
console.log(width, height);
box.style.width = '200px';
box.style.height = '200px';
4. 对复杂动画使用绝对定位
如果元素需要执行动画,且动画会改变布局属性,建议给元素设置position: absolute或position: fixed,让元素脱离文档流,这样元素的变化只会影响自身,不会触发其他元素的回流。
/* 动画元素样式 */
.animate-box {
position: absolute; /* 脱离文档流,减少回流影响范围 */
width: 50px;
height: 50px;
background-color: blue;
transition: all 0.3s ease;
}
.animate-box:hover {
transform: translateX(100px); /* 使用transform代替left等属性,减少回流 */
}
5. 避免频繁操作布局属性
尽量减少对width、height、margin、padding等会影响布局的属性的修改,优先使用transform、opacity等只会触发合成阶段、不会触发回流重绘的属性来实现视觉效果。
其他辅助优化技巧
- 将需要多次操作的DOM元素先设置为
display: none,操作完成后再显示,这样中间的操作不会触发回流,因为隐藏的元素不会参与渲染。 - 避免使用
table布局,表格的任何一个单元格变化都会触发整个表格的回流,布局成本更高。 - 使用
will-change属性提前告知浏览器元素可能发生的变化,让浏览器提前做好优化准备,但注意不要过度使用。
| 操作类型 | 是否触发回流 | 是否触发重绘 |
|---|---|---|
修改color、background-color等外观属性 | 否 | 是 |
修改width、height、margin等布局属性 | 是 | 是 |
使用transform、opacity修改元素 | 否 | 否 |
读取offsetWidth、clientHeight等属性 | 可能触发(强制同步布局) | 否 |
需要注意的是,现代浏览器的渲染引擎已经做了很多回流优化,比如将多次样式修改合并为一次回流,但开发者仍然需要遵循上述优化原则,避免写出明显影响性能的代码,尤其是在处理大量DOM操作的场景下,优化效果会更加明显。