vue 实现长列表
Vue 实现长列表的优化方案
在Vue中处理长列表时,直接渲染所有DOM节点会导致性能问题。以下是几种常见的优化方案:
虚拟滚动(Virtual Scrolling)
虚拟滚动只渲染可视区域内的列表项,大幅减少DOM节点数量:
<template>
<div class="virtual-list" @scroll="handleScroll">
<div class="scroll-container" :style="{ height: totalHeight + 'px' }">
<div
v-for="item in visibleItems"
:key="item.id"
class="list-item"
:style="{ transform: `translateY(${item.offset}px)` }"
>
{{ item.content }}
</div>
</div>
</div>
</template>
<script>
export default {
data() {
return {
allItems: [], // 所有数据
visibleItems: [], // 可视区域数据
itemHeight: 50, // 每项高度
startIndex: 0, // 起始索引
endIndex: 0, // 结束索引
buffer: 5 // 缓冲项数
}
},
computed: {
totalHeight() {
return this.allItems.length * this.itemHeight
}
},
methods: {
handleScroll(e) {
const scrollTop = e.target.scrollTop
this.startIndex = Math.floor(scrollTop / this.itemHeight) - this.buffer
this.endIndex = Math.ceil(
(scrollTop + e.target.clientHeight) / this.itemHeight
) + this.buffer
this.startIndex = Math.max(0, this.startIndex)
this.endIndex = Math.min(this.allItems.length - 1, this.endIndex)
this.updateVisibleItems()
},
updateVisibleItems() {
this.visibleItems = this.allItems.slice(this.startIndex, this.endIndex + 1)
.map((item, index) => ({
...item,
offset: (this.startIndex + index) * this.itemHeight
}))
}
}
}
</script>
使用现成库
推荐使用成熟的虚拟滚动库,如vue-virtual-scroller:

-
安装依赖:
npm install vue-virtual-scroller -
基本用法:

<template> <RecycleScroller class="scroller" :items="items" :item-size="50" key-field="id" v-slot="{ item }" > <div class="item"> {{ item.name }} </div> </RecycleScroller> </template>
export default { components: { RecycleScroller }, data() { return { items: [] // 你的长列表数据 } } }
```分页加载
对于不要求连续滚动的场景,可以采用分页加载:
<template>
<div>
<div v-for="item in currentItems" :key="item.id">
{{ item.content }}
</div>
<button @click="loadMore" v-if="hasMore">加载更多</button>
</div>
</template>
<script>
export default {
data() {
return {
allItems: [],
pageSize: 20,
currentPage: 0
}
},
computed: {
currentItems() {
return this.allItems.slice(0, (this.currentPage + 1) * this.pageSize)
},
hasMore() {
return this.currentItems.length < this.allItems.length
}
},
methods: {
loadMore() {
this.currentPage++
}
}
}
</script>
无限滚动
结合虚拟滚动和分页加载的优点:
<template>
<div class="infinite-list" @scroll="handleScroll">
<div v-for="item in loadedItems" :key="item.id">
{{ item.content }}
</div>
<div v-if="loading">加载中...</div>
</div>
</template>
<script>
export default {
data() {
return {
allItems: [],
loadedItems: [],
chunkSize: 30,
loading: false
}
},
mounted() {
this.loadNextChunk()
},
methods: {
handleScroll(e) {
const { scrollTop, clientHeight, scrollHeight } = e.target
if (scrollHeight - (scrollTop + clientHeight) < 50 && !this.loading) {
this.loadNextChunk()
}
},
loadNextChunk() {
this.loading = true
// 模拟异步加载
setTimeout(() => {
const start = this.loadedItems.length
const end = start + this.chunkSize
this.loadedItems = [
...this.loadedItems,
...this.allItems.slice(start, end)
]
this.loading = false
}, 500)
}
}
}
</script>
性能优化建议
- 避免在列表项中使用复杂的计算属性
- 为列表项设置固定的高度
- 使用
v-bind绑定静态数据而非动态计算 - 对于复杂列表项,考虑使用
v-once指令 - 合理使用
key属性,避免使用索引作为key
选择哪种方案取决于具体需求,虚拟滚动适合需要平滑滚动的超长列表,分页加载适合可以分批查看内容的场景,而无限滚动则结合了两者的优点。






