Glittering's blog Glittering's blog
Home
  • 学习手册

    • 《JavaScript教程》
    • 《TypeScript教程》
    • 《Git》
    • 《Vite》
    • 《Vue3》
    • 《React18》
    • 《CSS》
    • 《Tailwind CSS》
    • 《ES6 教程》
    • 《TypeScript 从零实现 axios》
  • 技术文档
  • 算法
  • 工作总结
  • 实用技巧
  • collect
About
  • Classification
  • Label
GitHub (opens new window)

Glitz Ma

前端开发工程师
Home
  • 学习手册

    • 《JavaScript教程》
    • 《TypeScript教程》
    • 《Git》
    • 《Vite》
    • 《Vue3》
    • 《React18》
    • 《CSS》
    • 《Tailwind CSS》
    • 《ES6 教程》
    • 《TypeScript 从零实现 axios》
  • 技术文档
  • 算法
  • 工作总结
  • 实用技巧
  • collect
About
  • Classification
  • Label
GitHub (opens new window)
  • 技术文档

  • 算法

  • 工作总结

    • 时区校正
    • 上传下载文件方式总结
    • web异常监控和分析
    • 前端优化指南
    • http缓存机制
    • 静态资源灰度发布
    • 浏览器原理及渲染机制
    • Chrome DevTools 渲染分析实战
    • Layout Thrashing(布局抖动)
      • ✅ 方案一:读写分离(最重要)
      • ✅ 方案二:批量写入(DOM 批处理)
      • ✅ 方案三:使用 transform 替代布局属性
      • ✅ 方案四:requestAnimationFrame 调度
      • ✅ 方案五:虚拟 DOM / 批量更新
        • Q:什么是 Layout Thrashing?
        • Q:如何避免?
    • Composite Layer(合成层)
    • 全局设置滚动条样式好吗?
    • 虚拟列表如何避免Layout和Paint
    • 前端安全知识
    • 安全(同源策略 / CSP / CORS)
    • 浏览器安全模型
    • 从chrome v8 讲安全
    • WebAssembly(Wasm)
    • XSS → JIT → 沙箱逃逸
    • 微前端总结
    • websocket聊天
    • axios 与 promise
    • react高级特性
    • react基础知识总结
    • vue2常见原理总结
    • vue2基础知识总结
    • webpack优化实践
    • webpack基础应用知识总结
    • 小程序笔记
    • 小程序工程模板设计
    • 地图标绘--射线法来计算点在多边形内
  • 实用技巧

  • 收藏夹

  • 技术
  • 工作总结
mamingjuan
2022-11-20
目录

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

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

写 width → 布局脏了
读 offsetWidth → 强制 Layout
写 width → 布局脏了
读 offsetWidth → 强制 Layout
……
1
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

一读这些:

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


# 四、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️⃣ Performance 面板分析

你会看到:

Task
 ├── Function Call
 │    ├── offsetWidth
 │    ├── Layout
 │    ├── offsetWidth
 │    ├── Layout
 │    ├── ...
1
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

👉 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

✔️ 跳过 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

📌 把多次修改分散到多帧


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

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

多次 setState
   ↓
合并
   ↓
一次真实 DOM 更新
1
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
Chrome DevTools 渲染分析实战
Composite Layer(合成层)

← Chrome DevTools 渲染分析实战 Composite Layer(合成层)→

Copyright © 2015-2026 Glitz Ma
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式