Skip to content

一、什么是 Layout Thrashing(一句话版)

在一次 JS 执行中,反复交替「写样式 → 读布局」, 迫使浏览器不断同步计算 Layout,导致性能急剧下降。

关键词:

  • ❌ 反复 Layout
  • ❌ 同步阻塞
  • ❌ FPS 暴跌

二、最典型的 Layout Thrashing 代码

js
for (let i = 0; i < 1000; i++) {
  el.style.width = el.offsetWidth + 1 + 'px'
}

看似无害,实际上发生了什么?

写 width → 布局脏了
读 offsetWidth → 强制 Layout
写 width → 布局脏了
读 offsetWidth → 强制 Layout
……

👉 1000 次 Layout

三、为什么会这样?(浏览器底层原理)

1️⃣ 浏览器的“惰性布局策略”

浏览器不会每改一次样式就重新 Layout:

  • 修改样式 → 标记为 dirty

  • 直到:

    • 要渲染
    • JS 读取布局信息

2️⃣ 哪些操作会“强制同步 Layout”?

📏 读取几何信息(必背)

js
offsetWidth / offsetHeight
offsetTop / offsetLeft
clientWidth / clientHeight
scrollTop / scrollHeight
getComputedStyle()
getBoundingClientRect()

一读这些:

浏览器必须立刻算出真实布局

四、DevTools 实战:亲眼看 Layout Thrashing

1️⃣ 用这个 Demo

html
<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>

2️⃣ Performance 面板分析

你会看到:

Task
 ├── Function Call
 │    ├── offsetWidth
 │    ├── Layout
 │    ├── offsetWidth
 │    ├── Layout
 │    ├── ...

📌 黄色 JS + 紫色 Layout 交错出现

👉 这就是 Layout Thrashing 的铁证

五、如何解决?(工程级方案)

✅ 方案一:读写分离(最重要)

js
let width = box.offsetWidth

for (let i = 0; i < 300; i++) {
  width += 1
}

box.style.width = width + 'px'

👉 1 次 Layout

✅ 方案二:批量写入(DOM 批处理)

js
box.style.cssText += 'width:200px;height:200px'

✅ 方案三:使用 transform 替代布局属性

js
// ❌
box.style.left = '200px'

// ✅
box.style.transform = 'translateX(200px)'

✔️ 跳过 Layout & Paint

✅ 方案四:requestAnimationFrame 调度

js
let width = box.offsetWidth

function step() {
  width += 1
  box.style.width = width + 'px'
  requestAnimationFrame(step)
}

step()

📌 把多次修改分散到多帧

✅ 方案五:虚拟 DOM / 批量更新

这也是 React / Vue 为什么快的原因之一:

多次 setState

合并

一次真实 DOM 更新

六、进阶:浏览器是怎么“记账”的?

浏览器内部维护:

  • Style dirty
  • Layout dirty
  • Paint dirty

只有在必要时才:

  • Style Recalc
  • Layout
  • Paint

👉 JS 读布局 = 强制清账

七、面试高频问法

Q:什么是 Layout Thrashing?

在 JS 执行过程中频繁交替读取布局信息和修改样式, 导致浏览器被迫多次同步执行 Layout,严重影响性能。

Q:如何避免?

  • 读写分离
  • 合并 DOM 操作
  • 使用 transform
  • 使用 rAF(requestAnimationFrame)
  • 使用框架的批量更新机制