Layout Thrashing(布局抖动)
# 一、什么是 Layout Thrashing(一句话版)
在一次 JS 执行中,反复交替「写样式 → 读布局」, 迫使浏览器不断同步计算 Layout,导致性能急剧下降。
关键词:
- ❌ 反复 Layout
- ❌ 同步阻塞
- ❌ FPS 暴跌
# 二、最典型的 Layout Thrashing 代码
for (let i = 0; i < 1000; i++) {
el.style.width = el.offsetWidth + 1 + 'px'
}
1
2
3
2
3
# 看似无害,实际上发生了什么?
写 width → 布局脏了
读 offsetWidth → 强制 Layout
写 width → 布局脏了
读 offsetWidth → 强制 Layout
……
1
2
3
4
5
2
3
4
5
👉 1000 次 Layout
# 三、为什么会这样?(浏览器底层原理)
# 1️⃣ 浏览器的“惰性布局策略”
浏览器不会每改一次样式就重新 Layout:
修改样式 → 标记为 dirty
直到:
- 要渲染
- 或 JS 读取布局信息
# 2️⃣ 哪些操作会“强制同步 Layout”?
# 📏 读取几何信息(必背)
offsetWidth / offsetHeight
offsetTop / offsetLeft
clientWidth / clientHeight
scrollTop / scrollHeight
getComputedStyle()
getBoundingClientRect()
1
2
3
4
5
6
2
3
4
5
6
一读这些:
浏览器必须立刻算出真实布局
# 四、DevTools 实战:亲眼看 Layout Thrashing
# 1️⃣ 用这个 Demo
<button id="btn">Run</button>
<div id="box" style="width:100px;height:100px;background:red"></div>
<script>
const box = document.getElementById('box')
btn.onclick = () => {
for (let i = 0; i < 300; i++) {
box.style.width = box.offsetWidth + 1 + 'px'
}
}
</script>
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
# 2️⃣ Performance 面板分析
你会看到:
Task
├── Function Call
│ ├── offsetWidth
│ ├── Layout
│ ├── offsetWidth
│ ├── Layout
│ ├── ...
1
2
3
4
5
6
7
2
3
4
5
6
7
📌 黄色 JS + 紫色 Layout 交错出现
👉 这就是 Layout Thrashing 的铁证
# 五、如何解决?(工程级方案)
# ✅ 方案一:读写分离(最重要)
let width = box.offsetWidth
for (let i = 0; i < 300; i++) {
width += 1
}
box.style.width = width + 'px'
1
2
3
4
5
6
7
2
3
4
5
6
7
👉 1 次 Layout
# ✅ 方案二:批量写入(DOM 批处理)
box.style.cssText += 'width:200px;height:200px'
1
# ✅ 方案三:使用 transform 替代布局属性
// ❌
box.style.left = '200px'
// ✅
box.style.transform = 'translateX(200px)'
1
2
3
4
5
2
3
4
5
✔️ 跳过 Layout & Paint
# ✅ 方案四:requestAnimationFrame 调度
let width = box.offsetWidth
function step() {
width += 1
box.style.width = width + 'px'
requestAnimationFrame(step)
}
step()
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
📌 把多次修改分散到多帧
# ✅ 方案五:虚拟 DOM / 批量更新
这也是 React / Vue 为什么快的原因之一:
多次 setState
↓
合并
↓
一次真实 DOM 更新
1
2
3
4
5
2
3
4
5
# 六、进阶:浏览器是怎么“记账”的?
浏览器内部维护:
- Style dirty
- Layout dirty
- Paint dirty
只有在必要时才:
- Style Recalc
- Layout
- Paint
👉 JS 读布局 = 强制清账
# 七、面试高频问法
# Q:什么是 Layout Thrashing?
在 JS 执行过程中频繁交替读取布局信息和修改样式, 导致浏览器被迫多次同步执行 Layout,严重影响性能。
# Q:如何避免?
- 读写分离
- 合并 DOM 操作
- 使用 transform
- 使用 rAF(requestAnimationFrame)
- 使用框架的批量更新机制
上次更新: 2026/01/07, 09:20:46