浏览器的重排(Reflow)和重绘(Repaint)是什么?如何优化?
概念
重排(Reflow):元素的大小、位置发生变化,浏览器重新计算布局。
重绘(Repaint):元素外观变化(颜色、背景等),但不影响布局,浏览器重新绘制。
重排一定会触发重绘,重绘不一定会触发重排触发重排的操作
- 添加/删除可见 DOM 元素
- 元素位置、尺寸变化(width、height、margin、padding)
- 内容变化(文字数量、图片大小)
- 窗口 resize
- 激活 CSS 伪类(:hover)
- 计算
offsetWidth、scrollTop等布局属性 - 设置
style属性
触发重绘的操作
- color、background、border-color
- box-shadow、text-decoration
- visibility(不触发布局变化)
优化策略
1. 减少 DOM 操作
// ❌ 每次操作都触发重排
for (let i = 0; i < 100; i++) {
el.style.left = i + 'px'
}
// ✅ 使用 transform(只触发合成)
el.style.transform = 'translateX(100px)'
// ✅ 使用 class 替代多次 style 操作
el.className = 'moved' // 一次重排2. 批量 DOM 修改
// ❌ 多次读写交替触发强制重排
const width = el.offsetWidth // 读
el.style.width = width + 10 + 'px' // 写
const height = el.offsetHeight // 读(强制重排)
el.style.height = height + 10 + 'px' // 写
// ✅ 先读后写
const width = el.offsetWidth // 读
const height = el.offsetHeight // 读
el.style.width = width + 10 + 'px' // 写
el.style.height = height + 10 + 'px' // 写3. 使用 DocumentFragment
const fragment = document.createDocumentFragment()
for (let i = 0; i < 100; i++) {
const li = document.createElement('li')
li.textContent = `Item ${i}`
fragment.appendChild(li)
}
ul.appendChild(fragment) // 只触发一次重排4. 隐藏 → 修改 → 显示
el.style.display = 'none' // 一次重排
// 大量修改...
el.style.display = 'block' // 一次重排5. 使用 transform 和 opacity
/* ❌ 触发重排 */
.animate { left: 100px; top: 100px; }
/* ✅ 只触发合成层 */
.animate { transform: translate(100px, 100px); }6. 使用 will-change
.animated-element {
will-change: transform; /* 提前创建合成层 */
}7. 使用 requestAnimationFrame
function animate() {
// 在帧开始时执行
requestAnimationFrame(() => {
el.style.transform = 'translateX(100px)'
})
}强制同步布局(Layout Thrashing)
// ❌ 读后写交替,导致强制重排
elements.forEach(el => {
const height = el.offsetHeight // 读 → 触发重排
el.style.height = height + 10 + 'px' // 写 → 使下一次读触发重排
})
// ✅ 分离读写
const heights = elements.map(el => el.offsetHeight) // 全部读
elements.forEach((el, i) => {
el.style.height = heights[i] + 10 + 'px' // 全部写
})只读属性会触发重排
offsetWidth、offsetHeight、clientWidth、scrollTop、getComputedStyle() 等会强制浏览器执行待处理的布局变更,应尽量减少调用。