🗓️ 目标:100 篇
✅ 已完成:0 篇
📍 当前:Day 01 - 重排(Reflow)与重绘(Repaint)
本篇深入聊聊前端性能优化中的 重排(Reflow)与重绘(Repaint),这是每个前端工程师都应该掌握的底层知识之一。
🔍 一、为什么了解重排和重绘很重要?
浏览器是一个复杂的渲染引擎,它需要不断地把 HTML、CSS 和 JS 合成出一个用户可以交互的界面。我们写的代码会触发浏览器执行一系列渲染步骤:
HTML 解析 → DOM 构建
CSS 解析 → CSSOM 构建
DOM + CSSOM → 渲染树(Render Tree)
渲染树布局(Layout/Reflow)
像素绘制(Paint/Repaint)
合成图层 → 显示
其中,Reflow 和 Repaint 是最容易被忽略却最影响性能的两个步骤。
🧱 二、什么是重排(Reflow)?
定义: 当页面的结构或元素的位置、大小发生变化时,浏览器会重新计算元素的位置和几何信息,这就是 重排。
✅ 会触发重排的操作有:
-
添加、删除 DOM 节点
-
改变元素的尺寸(
width、height、padding、margin、border) -
改变元素的显示状态(如
display: none→block) -
改变页面布局的属性(如
position,top,left,flex,grid等) -
读取布局属性(触发强制同步):
offsetWidth,offsetHeight,clientTop,scrollTop,getBoundingClientRect()等等
💡 一些方法读取布局信息时会强制浏览器先做一次 Reflow,以确保获取的是最新值。
🎨 三、什么是重绘(Repaint)?
定义: 当元素的外观改变(但没有改变布局)时,浏览器会重新绘制该元素的像素。
✅ 会触发重绘的操作有:
-
改变
color、background-color -
改变
visibility、box-shadow、border-color -
改变文字颜色、字体(不会影响布局)
⚠️ 四、重排和重绘的性能影响
-
重排 是代价最高的,因为它会影响整个页面的渲染树,尤其是在嵌套结构较多、DOM 较大的情况下。
-
重绘 相对轻一些,但也不免费,尤其是在动画或频繁触发时。
📌 性能杀手:频繁修改 DOM + 频繁读取布局属性 + 大量样式变动 → 卡顿、掉帧!
🛠️ 五、优化技巧
✅ 1. 减少 DOM 操作频率
// 错误写法:连续操作 DOM
for (let i = 0; i < 1000; i++) {
let node = document.createElement('div');
document.body.appendChild(node);
}
// 更好的方式:
let fragment = document.createDocumentFragment();
for (let i = 0; i < 1000; i++) {
let node = document.createElement('div');
fragment.appendChild(node);
}
document.body.appendChild(fragment);
✅ 2. 批量修改样式
// 错误:会触发多次重绘/重排
el.style.width = '100px';
el.style.height = '100px';
el.style.backgroundColor = 'red';
// 正确:一次性改
Object.assign(el.style, {
width: '100px',
height: '100px',
backgroundColor: 'red'
});
✅ 3. 避免同步布局读取(强制 reflow)
// 错误做法(会强制刷新布局)
box.style.width = '200px';
console.log(box.offsetHeight); // 读取会导致强制 reflow
// 更好做法:读写分离
let height = box.offsetHeight;
box.style.width = '200px';
✅ 4. 使用 classList 替代 style 连续修改
// 推荐用 classList 来统一样式变化
element.classList.add('active');
✅ 5. CSS 动画比 JS 更高效
/* 更高效的动画:GPU 加速 */
.box {
transition: transform 0.3s ease;
will-change: transform; /* 告诉浏览器“我要用 transform 动画了,请提前准备图层!” */
}
.box:hover {
transform: translate3d(10px, 0, 0); /* 启用 GPU 加速 */
}
❌ 滥用 will-change(提前合成图层过多)
.card { will-change: transform; }问题: 虽然
will-change是为了优化性能,但如果你给太多元素加上它,浏览器会:
提前分配显存
创建过多合成层
内存暴涨甚至卡顿
✅ 正确做法:
只在即将发生动画或变换的元素上使用
动画完成后,移除
will-change// 动态加 will-change 更合理 element.style.willChange = 'transform'; setTimeout(() => { element.style.willChange = ''; }, 500);
🧠 六、动画时的最佳实践
动画时尤其要注意重绘和重排,因为它每秒要运行 60 帧(每帧 <16ms),稍微不注意就会掉帧!
💡 最佳选择:使用
transform和opacity做动画,它们不会引起重排,也通常不会引起重绘,而是由合成层(compositing)来处理图层位移或透明度,性能最优。
🎁 七、总结一句话:
“能不动 DOM,就别动;能不改样式,就别改;要改,就一次改完。”