虚拟列表(Virtual List)是一种优化长列表渲染性能的技术,其核心思想是仅渲染当前视窗(viewport)内的可见项,而非整个列表,从而大幅减少 DOM 节点数量,提升页面性能。
虚拟列表的作用
- 性能优化
- 避免渲染大量不可见的 DOM 节点,减少内存占用和布局计算(Reflow/Repaint)的开销。
- 尤其适用于移动端或数据量大的场景(如千级、万级列表)。
- 流畅滚动
- 减少 DOM 操作,使滚动更流畅,避免卡顿。
- 节省资源
- 降低 CPU 和内存使用率,提升应用整体响应速度。
Vue 中实现虚拟列表的方案
1. 手动实现(基于 scroll 事件和动态渲染)
核心逻辑:
- 计算视窗高度和可显示项的数量。
- 监听滚动事件,动态计算当前视窗的起始和结束索引。
- 只渲染可见范围内的列表项,并通过
transform: translateY模拟滚动位置。
示例代码:
vue
1<template>
2 <div class="virtual-list-container" @scroll="handleScroll">
3 <div class="virtual-list-phantom" :style="{ height: totalHeight + 'px' }"></div>
4 <div class="virtual-list" :style="{ transform: `translateY(${offset}px)` }">
5 <div
6 v-for="item in visibleItems"
7 :key="item.id"
8 class="virtual-list-item"
9 >
10 {{ item.text }}
11 </div>
12 </div>
13 </div>
14</template>
15
16<script>
17export default {
18 data() {
19 return {
20 items: Array.from({ length: 10000 }, (_, i) => ({ id: i, text: `Item ${i}` })),
21 itemHeight: 50, // 每个列表项的高度(固定)
22 visibleCount: 10, // 视窗内可见的项数
23 offset: 0, // 当前偏移量
24 startIndex: 0, // 起始索引
25 };
26 },
27 computed: {
28 totalHeight() {
29 return this.items.length * this.itemHeight;
30 },
31 visibleItems() {
32 const endIndex = Math.min(this.startIndex + this.visibleCount, this.items.length);
33 return this.items.slice(this.startIndex, endIndex);
34 },
35 },
36 methods: {
37 handleScroll({ target }) {
38 const scrollTop = target.scrollTop;
39 this.startIndex = Math.floor(scrollTop / this.itemHeight);
40 this.offset = this.startIndex * this.itemHeight;
41 },
42 },
43};
44</script>
45
46<style>
47.virtual-list-container {
48 position: relative;
49 height: 500px; /* 视窗高度 */
50 overflow: auto;
51}
52.virtual-list-phantom {
53 position: absolute;
54 left: 0;
55 top: 0;
56 right: 0;
57 z-index: -1; /* 撑开容器高度 */
58}
59.virtual-list {
60 position: absolute;
61 left: 0;
62 right: 0;
63 top: 0;
64}
65.virtual-list-item {
66 height: 50px;
67 line-height: 50px;
68 border-bottom: 1px solid #eee;
69}
70</style>
71
2. 使用第三方库
Vue 生态中有成熟的虚拟列表库,推荐以下两种:
(1)vue-virtual-scroller
- 支持动态高度、滚动条、复用组件等高级功能。
- 安装:
bash
1npm install vue-virtual-scroller 2 - 使用:
vue
1<template> 2 <RecycleScroller 3 class="scroller" 4 :items="items" 5 :item-size="50" 6 key-field="id" 7 v-slot="{ item }" 8 > 9 <div class="item">{{ item.text }}</div> 10 </RecycleScroller> 11</template> 12 13<script> 14import { RecycleScroller } from 'vue-virtual-scroller'; 15import 'vue-virtual-scroller/dist/vue-virtual-scroller.css'; 16 17export default { 18 components: { RecycleScroller }, 19 data() { 20 return { 21 items: Array.from({ length: 10000 }, (_, i) => ({ id: i, text: `Item ${i}` })), 22 }; 23 }, 24}; 25</script> 26 27<style> 28.scroller { 29 height: 500px; 30} 31.item { 32 height: 50px; 33 border-bottom: 1px solid #eee; 34} 35</style> 36
(2)vue-virtual-scroll-list
- 轻量级,适合固定高度的列表。
- 安装:
bash
1npm install vue-virtual-scroll-list 2 - 使用:
vue
1<template> 2 <virtual-list 3 :size="50" 4 :remain="8" 5 :data-key="'id'" 6 :data-sources="items" 7 :data-component="itemComponent" 8 /> 9</template> 10 11<script> 12import VirtualList from 'vue-virtual-scroll-list'; 13import Item from './Item.vue'; // 自定义列表项组件 14 15export default { 16 components: { VirtualList }, 17 data() { 18 return { 19 items: Array.from({ length: 10000 }, (_, i) => ({ id: i, text: `Item ${i}` })), 20 itemComponent: Item, 21 }; 22 }, 23}; 24</script> 25
关键点总结
- 固定高度 vs 动态高度
- 固定高度:计算更简单,性能更好(如
vue-virtual-scroll-list)。 - 动态高度:需要额外缓存高度信息(如
vue-virtual-scroller的variable模式)。
- 固定高度:计算更简单,性能更好(如
- 滚动事件优化
- 使用
requestAnimationFrame或throttle避免频繁触发滚动事件。
- 使用
- 复用组件
- 通过
key和组件复用减少创建开销(如vue-virtual-scroller的v-slot)。
- 通过
- 边界处理
- 确保滚动到底部时正确加载数据,避免空白区域。
适用场景
- 长列表(如聊天记录、表格、瀑布流)。
- 数据量大且需要频繁滚动的页面。
- 移动端性能敏感场景。
通过虚拟列表,可以显著提升大数据量下的渲染性能,是前端优化的重要手段之一。