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(布局抖动)
    • Composite Layer(合成层)
    • 全局设置滚动条样式好吗?
    • 虚拟列表如何避免Layout和Paint
      • 一、虚拟列表到底在“避免”什么?
        • 如果不用虚拟列表(灾难现场)
        • 使用虚拟列表后
      • 二、虚拟列表的三大“避坑点”(核心原理)
        • ① DOM 数量控制(最重要)
        • ② 滚动用“位移”而不是“重排”
        • ❌ 错误做法(会 Layout)
        • ✅ 正确做法(只 Composite)
        • ③ 占位但不渲染(scroll illusion)
        • 核心结构
        • 浏览器视角
      • 三、完整渲染流程对比(非常重要)
        • 普通列表滚动
        • 虚拟列表滚动
      • 四、为什么滚动本身不会频繁 Layout?
        • 浏览器优化点
        • 但问题在于:
      • 五、变高列表(Variable Height)怎么办?
        • 方案一:固定高度(最快)
        • 方案二:高度缓存(主流方案)
        • 方案三:IntersectionObserver(现代方案)
      • 六、DevTools 怎么验证“真的没 Layout / Paint”?
        • 1️⃣ Performance 面板
        • 2️⃣ Paint flashing
        • 3️⃣ FPS meter
      • 七、React / Vue 为什么都推荐虚拟列表?
        • React(react-window)
        • Vue(vue-virtual-scroller)
      • 八、一个极简“虚拟列表核心算法”(理解用)
      • 九、总结
    • 前端安全知识
    • 安全(同源策略 / CSP / CORS)
    • 浏览器安全模型
    • 从chrome v8 讲安全
    • WebAssembly(Wasm)
    • XSS → JIT → 沙箱逃逸
    • 微前端总结
    • websocket聊天
    • axios 与 promise
    • react高级特性
    • react基础知识总结
    • vue2常见原理总结
    • vue2基础知识总结
    • webpack优化实践
    • webpack基础应用知识总结
    • 小程序笔记
    • 小程序工程模板设计
    • 地图标绘--射线法来计算点在多边形内
  • 实用技巧

  • 收藏夹

  • 技术
  • 工作总结
mamingjuan
2024-12-20
目录

虚拟列表如何避免Layout和Paint

虚拟列表(Virtual List / Windowing)的核心价值,本质就是:

👉 让浏览器“以为”列表很大,但实际上参与 Layout / Paint 的节点极少


# 一、虚拟列表到底在“避免”什么?

# 如果不用虚拟列表(灾难现场)

10,000 条 DOM
↓
每次滚动:
- Layout(全部参与)
- Paint(大量节点)
- Style Recalc
1
2
3
4
5
6

📉 FPS 直接崩


# 使用虚拟列表后

可视区 20 条 + buffer 10 条 = 30 条 DOM
↓
滚动:
- 少量 transform
- 少量 style 更新
- 几乎不 Layout
1
2
3
4
5
6

✔️ 性能线性 → 常数级


# 二、虚拟列表的三大“避坑点”(核心原理)

# ① DOM 数量控制(最重要)

Layout / Paint 的复杂度 ≈ DOM 数量

虚拟列表只渲染:

[ startIndex, endIndex ]
1

其余:

  • 不存在 DOM
  • 不参与 Render Tree
  • 不参与 Layout / Paint

# ② 滚动用“位移”而不是“重排”

# ❌ 错误做法(会 Layout)

item.style.top = index * itemHeight + 'px'
1
  • top → 影响布局
  • 每个 item 都可能触发布局计算

# ✅ 正确做法(只 Composite)

container.style.transform = `translateY(${offset}px)`
1
  • transform → 合成层
  • 跳过 Layout / Paint

📌 这是虚拟列表流畅的关键


# ③ 占位但不渲染(scroll illusion)

# 核心结构

<div class="viewport">
  <div class="phantom"></div>
  <div class="content"></div>
</div>
1
2
3
4
.viewport {
  overflow-y: auto;
  height: 400px;
}
.phantom {
  height: 100000px; /* 总高度 */
}
.content {
  position: absolute;
  transform: translateY(2000px);
}
1
2
3
4
5
6
7
8
9
10
11

# 浏览器视角

  • phantom:只参与一次 Layout
  • content:少量 DOM + transform

👉 滚动条真实存在,但 DOM 极少


# 三、完整渲染流程对比(非常重要)

# 普通列表滚动

scroll
 → Layout(全部子节点)
 → Paint(大量)
 → Composite
1
2
3
4

# 虚拟列表滚动

scroll
 → JS 计算 startIndex
 → transform 位移
 → Composite
1
2
3
4

📌 中间直接跳过 Layout / Paint


# 四、为什么滚动本身不会频繁 Layout?

# 浏览器优化点

  • 滚动 ≠ 重新计算布局

  • 只是:

    • 改变 scroll offset
    • 裁剪可视区域

# 但问题在于:

👉 子节点太多时,Paint 和 Composite 还是贵

虚拟列表直接:

  • 不让它们存在

# 五、变高列表(Variable Height)怎么办?

这是进阶难点。

# 方案一:固定高度(最快)

  • 最佳性能
  • 实现最简单

# 方案二:高度缓存(主流方案)

heightMap[index] = realHeight
1
  • 首次测量
  • 后续直接用缓存
  • 避免反复 getBoundingClientRect

📌 防止 Layout Thrashing


# 方案三:IntersectionObserver(现代方案)

  • 非同步测量
  • 减少强制 Layout
  • 异步感知可见性

# 六、DevTools 怎么验证“真的没 Layout / Paint”?

# 1️⃣ Performance 面板

  • 滚动过程中:

    • 几乎无 Layout
    • 少量 Composite

# 2️⃣ Paint flashing

  • 可视区域极少闪烁

# 3️⃣ FPS meter

  • 稳定 60

# 七、React / Vue 为什么都推荐虚拟列表?

# React(react-window)

  • 使用 transform
  • 合并 setState
  • rAF 调度

# Vue(vue-virtual-scroller)

  • 绝对定位 + translate
  • 缓存高度
  • 减少 watcher 触发

👉 本质都是:

控制 DOM + 用合成层


# 八、一个极简“虚拟列表核心算法”(理解用)

const itemHeight = 40
const visibleCount = Math.ceil(viewportHeight / itemHeight)

function onScroll(scrollTop) {
  const start = Math.floor(scrollTop / itemHeight)
  const end = start + visibleCount + buffer

  content.style.transform =
    `translateY(${start * itemHeight}px)`

  render(items.slice(start, end))
}
1
2
3
4
5
6
7
8
9
10
11
12

# 九、总结

  • 虚拟列表通过只渲染可视区附近的少量 DOM,
  • 大幅减少参与 Layout 和 Paint 的节点数量。
  • 同时使用 transform 进行位移,
  • 让滚动过程主要发生在 Composite 阶段,
  • 从而在大数据量列表中保持稳定的渲染性能。
上次更新: 2026/01/07, 09:20:46
全局设置滚动条样式好吗?
前端安全知识

← 全局设置滚动条样式好吗? 前端安全知识→

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