📖 正文
在前端开发中,大数据量列表渲染 一直是性能挑战。假设我们有一个 API 返回 1 万条数据 ,如果直接渲染到页面,必然会导致严重卡顿甚至浏览器崩溃。
解决方案就是 ------ 虚拟列表 (Virtual List) 。
虚拟列表的核心思想是:只渲染当前视口范围内的元素 ,通过计算位置来"假装"完整渲染。
常见实现方式主要有两种:
- Spacer 占位法
- Absolute 定位法
本文将对比这两种实现,并给出适用场景。
🔹 1. Spacer 占位法实现
思路:用 上空白块 + 下空白块 撑出容器总高度,视口中只渲染真实元素。
xml
<div ref="root" @scroll="onScroll"
:style="{ overflowY: 'auto', height: containerHeight + 'px' }">
<!-- 上占位 -->
<div :style="{ height: topSpacer + 'px' }"></div>
<!-- 可见元素 -->
<div v-for="item in visibleItems" :key="item.id" class="virtual-item">
{{ item.text }}
</div>
<!-- 下占位 -->
<div :style="{ height: bottomSpacer + 'px' }"></div>
</div>
👉 优点:
- 实现简单直观
- 支持动态高度(只要调整 spacer 值即可)
👉 缺点:
- 多余的 DOM(两个 spacer)
- 每次滚动都要更新
topSpacer
和bottomSpacer
,涉及 回流 (Reflow)
🔹 2. Absolute 定位法实现
思路:先撑出一个 固定高度的容器 ,每个 item 通过 absolute + top
定位。
xml
<div ref="root" @scroll="onScroll"
:style="{ overflowY: 'auto', height: containerHeight + 'px', position: 'relative' }">
<!-- 总高度容器 -->
<div :style="{ height: totalHeight + 'px', position: 'relative' }">
<div v-for="(item, idx) in visibleItems"
:key="item.id"
class="virtual-item"
:style="{ position: 'absolute', top: (startIndex + idx) * itemHeight + 'px' }">
{{ item.text }}
</div>
</div>
</div>
👉 优点:
- DOM 结构更简洁(没有 spacer)
- 绝对定位减少回流,性能更好
- 更适合大数据场景(5w+ 条时依然稳定)
👉 缺点:
- 对自适应高度支持不友好(必须固定高度)
position: sticky
等布局特性会失效
🔹 3. 性能对比
对比维度 | Spacer 占位法 | Absolute 定位法 |
---|---|---|
DOM 结构 | 多 2 个占位元素 | 更简洁 |
回流影响 | spacer 高度频繁变化,可能触发回流 | 元素绝对定位,回流更少 |
动态高度 | 支持,容易实现 | 支持困难,需要累积高度映射 |
大数据量 (10w+) | 可能浮点误差 | 更稳,FPS 更高 |
布局特性 | 流式布局更自然 | 绝对定位可能受限 |
🔹 4. 实践建议
- 固定高度 item → 推荐 Absolute 定位法
简洁高效,减少 DOM & 回流开销。 - 动态高度 item → 推荐 Spacer 占位法
更容易适配复杂布局。 - 超大数据量 (5w+ 条) → Absolute 更稳
如果你要做一个通用组件,可以支持 两种实现方式切换:
- 默认使用 Absolute 定位法
- 如果检测到 item 高度不一致,再切换到 Spacer 占位法
这种混合策略,正是一些成熟虚拟列表库(如 react-window
、vue-virtual-scroll-list
)的做法。
🔹 5. 结论
- 两种方案都能解决虚拟列表问题
- Absolute 定位法 在固定高度场景下性能更优
- Spacer 占位法 更灵活,能支持动态高度
👉 所以,选择哪种方案要根据 业务需求 来定。