虚拟列表在 Vue3 中的核心优势是仅渲染可视区域的内容,大幅提升大数据量列表的性能。以下是具体应用场景及实现方式:
场景:展示成千上万条数据记录,传统渲染会导致页面卡顿。
实现:使用 vue-virtual-scroller 或自定义虚拟列表组件。
示例代码:
<template>
<div class="container">
<VirtualTable :items="largeData" :itemHeight="36">
<template #item="{ item }">
<div class="table-row">
<div class="cell">{{ item.id }}</div>
<div class="cell">{{ item.name }}</div>
<div class="cell">{{ item.date }}</div>
</div>
</template>
</VirtualTable>
</div>
</template>
<script setup>
import { ref } from 'vue'
import { VirtualTable } from 'vue-virtual-scroller'
import 'vue-virtual-scroller/dist/vue-virtual-scroller.css'
const largeData = ref(Array(10000).fill().map((_, i) => ({
id: i + 1,
name: `用户${i + 1}`,
date: new Date().toISOString(),
})))
</script>场景:长对话历史或群聊消息,需快速滚动浏览。
优化点:
示例:
<template>
<div class="chat-container">
<ListScroller :items="messages" :itemSize="getItemSize">
<template #item="{ item }">
<MessageComponent :message="item" />
</template>
</ListScroller>
</div>
</template>
<script setup>
import { ref } from 'vue'
import { ListScroller } from 'vue-virtual-scroller'
import MessageComponent from './MessageComponent.vue'
const messages = ref(generateLargeMessages(5000)) // 生成5000条消息
const getItemSize = (item) => {
// 根据消息类型和内容动态计算高度
return item.isImage ? 200 : 50
}
</script>场景:无限滚动的朋友圈、微博等内容流。
实现方案:
IntersectionObserver 预加载下一页数据。示例:
<template>
<div class="feed-container">
<VirtualList :items="posts" :itemHeight="300">
<template #item="{ item }">
<PostCard :post="item" />
</template>
</VirtualList>
<div v-if="loading" class="loading">加载中...</div>
</div>
</template>
<script setup>
import { ref, onMounted } from 'vue'
import { VirtualList } from 'vue-virtual-scroller'
import PostCard from './PostCard.vue'
const posts = ref([])
const loading = ref(false)
let page = 1
const loadMore = async () => {
if (loading.value) return
loading.value = true
const newPosts = await fetchPosts(page) // 模拟API请求
posts.value = [...posts.value, ...newPosts]
page++
loading.value = false
}
onMounted(() => {
loadMore()
})
</script>场景:长文本内容(如小说、论文)的分页或连续阅读。
优化点:
实现参考:
<template>
<div class="reader-container">
<VirtualList :items="pages" :itemHeight="screenHeight">
<template #item="{ item }">
<PageContent :content="item.content" />
</template>
</VirtualList>
</div>
</template>
<script setup>
import { ref, computed } from 'vue'
import { VirtualList } from 'vue-virtual-scroller'
import PageContent from './PageContent.vue'
const screenHeight = computed(() => window.innerHeight - 100) // 减去导航栏高度
const pages = ref(generateBookPages(largeTextContent, screenHeight.value))
</script>场景:显示全年日期、日程安排,需高效渲染大量日期格子。
实现方式:
示例结构:
<template>
<div class="calendar-container">
<VirtualGrid
:rows="31"
:cols="7"
:cellHeight="80"
:cellWidth="100"
>
<template #cell="{ row, col }">
<CalendarCell :date="getDate(row, col)" />
</template>
</VirtualGrid>
</div>
</template>
<script setup>
import { ref } from 'vue'
import { VirtualGrid } from 'vue-virtual-scroller'
import CalendarCell from './CalendarCell.vue'
</script>场景:组织架构图、文件管理器等嵌套层级极深的场景。
优化方案:
示例代码:
<template>
<div class="tree-container">
<VirtualTree :nodes="fileTree">
<template #node="{ node, depth }">
<div :style="{ paddingLeft: `${depth * 20}px` }">
<span>{{ node.name }}</span>
<button v-if="node.hasChildren" @click="loadChildren(node)">展开</button>
</div>
</template>
</VirtualTree>
</div>
</template>
<script setup>
import { ref } from 'vue'
import { VirtualTree } from 'vue-virtual-scroller'
const fileTree = ref(generateInitialTree())
const loadChildren = async (node) => {
if (node.children) return
node.children = await fetchChildren(node.id) // 异步加载子节点
}
</script>useVirtualList 组合式函数,适合自定义实现。通过虚拟列表,即使处理百万级数据,也能保持流畅的滚动体验,大幅提升用户体验。