Vue中虛擬列表的原理與實(shí)現(xiàn)詳解
前言
最近在工作中遇到了一個(gè)列表的需求,因?yàn)樽龅氖荂端,所以對(duì)性能體驗(yàn)要求很高,主要有下面幾點(diǎn)要求,也可以說是痛點(diǎn)
- 就算加載上千條數(shù)據(jù),也能夠立即加載,而不會(huì)卡頓,等待時(shí)間不能太長
- 加載好以后用戶能立馬進(jìn)行交互,而不是要等待,體驗(yàn)必須絲滑
仔細(xì)想想, 加載上千條數(shù)據(jù),加載時(shí)間肯定會(huì)相對(duì)長一些,而又要求立即加載,這不是一個(gè)矛盾的需求么,所以如果使用常規(guī)的操作,肯定難以解決這個(gè)痛點(diǎn),所以必須引出我們今天的主角才能解決今天的問題
什么是虛擬列表
虛擬列表是一種技術(shù),它只渲染用戶當(dāng)前可見的列表項(xiàng),而不是渲染整個(gè)列表。
虛擬列表的原理是什么
只渲染可視區(qū)域內(nèi)的列表項(xiàng),而不是渲染整個(gè)列表
當(dāng)用戶滾動(dòng)容器時(shí),虛擬列表會(huì)根據(jù)滾動(dòng)位置和可視區(qū)域的大小計(jì)算出當(dāng)前應(yīng)該顯示的列表項(xiàng)。
首先實(shí)現(xiàn)一個(gè)可以滑動(dòng)的列表
因?yàn)槲覀冎饕v的是虛擬列表,為了不增加大家負(fù)擔(dān),那么列表容器的高度,列表項(xiàng)的高度,我們都設(shè)置為已知的,而實(shí)際使用的時(shí)候,可以根據(jù)場景,動(dòng)態(tài)獲取
代碼很簡單,然后我們一次性渲染10萬條數(shù)據(jù)數(shù)據(jù)
<template>
<div class="about">
<div class="scrollView">
<div class="list">
<div class="item" v-for="item in list" :key="item">
{{ item }}
</div>
</div>
</div>
</div>
</template>
<script>
export default {
data() {
return {
list: [],
};
},
mounted() {
const total = 100000;
const list = [];
for (let i = 0; i < total; i++) {
list.push(i);
}
this.list = list;
}
};
</script>
<style>
.scrollView {
width: 200px;
height: 300px;
background-color: red;
overflow-y: scroll;
position: relative;
}
.item {
height: 50px;
}
</style>
測(cè)試結(jié)果非常恐怖,因?yàn)閿?shù)據(jù)量龐大,發(fā)現(xiàn)才開始的時(shí)候一直卡著不動(dòng),有1s多的時(shí)間是什么也看不到的,這無疑是致命的
實(shí)現(xiàn)虛擬列表
列表高度為300,列表項(xiàng)高度為50,那么我們只需要展示6條數(shù)據(jù)就可以了,因?yàn)橛锌赡苌侠瓟?shù)據(jù),那么有可能展示第7條數(shù)據(jù),所以我們要展示7條數(shù)據(jù),但是首次加載我們只用展示6條
所以此時(shí)我們改一下mounted生命周期
mounted() {
const total = 100000;
const list = [];
for (let i = 0; i < total; i++) {
list.push(i);
}
this.data = list;
this.list = list.slice(0, 6);
},問題一:我們只展示可視區(qū)域的列表,那么如何顯示滾動(dòng)條呢
這個(gè)問題就是一個(gè)關(guān)鍵,需要我們?cè)O(shè)置一個(gè)虛擬的列表,定義好高度
<template>
<div class="about">
<div class="scrollView" @scroll="onScroll" ref="list">
<!-- 虛擬列表 -->
<div class="virtualScroller" :style="{ height: listHeight + 'px' }"></div>
<div class="list">
<div class="item" v-for="item in list" :key="item">
{{ item }}
</div>
</div>
</div>
</div>
</template>
<script>
export default {
data() {
return {
list: [],
listHeight: 0,
itemSize: 50,
containerHeight: 300,
visibleCount: 6,
data: [],
startOffset: 0,
};
},
mounted() {
....
this.listHeight = list.length * this.itemSize;
.....
}
};
</script>
<style>
.list {
position: absolute;
top: 0;
left: 0;
}
</style>問題二:下拉列表以后,如何讓列表內(nèi)容顯示到可視區(qū)域內(nèi)

現(xiàn)在我們的效果是這樣的,下拉的時(shí)候,列表內(nèi)容都上移了
所以我們要讓列表內(nèi)容在滾動(dòng)的時(shí)候,移動(dòng)到可視區(qū)域內(nèi)
我們監(jiān)聽scrollView的 scroll事件
<div class="scrollView" @scroll="onScroll" ref="list">
onScroll的實(shí)現(xiàn)包括以下幾個(gè)內(nèi)容
- 滾動(dòng)距離:
scrollTop - 列表開始索引:
startIndex - 列表結(jié)束索引:
endIndex - 列表移動(dòng)距離:
startOffset: 這個(gè)是重點(diǎn)
this.startOffset = scrollTop - (scrollTop % this.itemSize);
所以我們的onScroll方法的實(shí)現(xiàn)是
onScroll() {
//當(dāng)前滾動(dòng)位置
let scrollTop = this.$refs.list.scrollTop;
// 列表開始索引
let startIndex = Math.floor(scrollTop / this.itemSize);
// 列表結(jié)束索引
let endIndex = Math.ceil((scrollTop + this.containerHeight) / this.itemSize);
this.list = this.data.slice(startIndex, endIndex);
// 列表移動(dòng)距離
this.startOffset = scrollTop - (scrollTop % this.itemSize);
}此時(shí)我們的虛擬列表就實(shí)現(xiàn)了
完整代碼如下:
<template>
<div class="about">
<div class="scrollView" @scroll="onScroll" ref="list">
<!-- 虛擬列表 -->
<div class="virtualScroller" :style="{ height: listHeight + 'px' }"></div>
<div class="list" :style="{ transform: `translateY(${this.startOffset}px)` }">
<div class="item" v-for="item in list" :key="item">
{{ item }}
</div>
</div>
</div>
</div>
</template>
<script>
export default {
data() {
return {
list: [],
listHeight: 0,
itemSize: 50,
containerHeight: 300,
visibleCount: 6,
data: [],
startOffset: 0,
};
},
mounted() {
const total = 100000;
const list = [];
for (let i = 0; i < total; i++) {
list.push(i);
}
this.listHeight = list.length * this.itemSize;
this.data = list;
this.list = list.slice(0, 6);
},
methods: {
onScroll() {
//當(dāng)前滾動(dòng)位置
let scrollTop = this.$refs.list.scrollTop;
// 列表開始索引
let startIndex = Math.floor(scrollTop / this.itemSize);
// 列表結(jié)束索引
let endIndex = Math.ceil((scrollTop + this.containerHeight) / this.itemSize);
this.list = this.data.slice(startIndex, endIndex);
// 列表移動(dòng)距離
this.startOffset = scrollTop - (scrollTop % this.itemSize);
}
}
};
</script>
<style>
.scrollView {
width: 200px;
height: 300px;
background-color: red;
overflow-y: scroll;
position: relative;
}
.item {
height: 50px;
}
.list {
position: absolute;
top: 0;
left: 0;
}
</style>那么虛擬列表和下拉加載更多有什么區(qū)別呢
虛擬列表(Virtual List)和下拉加載更多(Infinite Scroll)都是用于優(yōu)化長列表或大數(shù)據(jù)集的用戶界面的技術(shù),但它們有一些區(qū)別。
虛擬列表通過動(dòng)態(tài)渲染當(dāng)前可見的列表項(xiàng)來提高性能和內(nèi)存利用率,而下拉加載更多是在用戶滾動(dòng)到列表底部時(shí)自動(dòng)加載更多數(shù)據(jù)。兩者都是為了優(yōu)化大數(shù)據(jù)集或長列表的用戶界面,提供更好的性能和用戶體驗(yàn)。具體選擇哪種技術(shù)取決于具體的需求和場景。
參考
「前端進(jìn)階」高性能渲染十萬條數(shù)據(jù)(虛擬列表)
到此這篇關(guān)于Vue中虛擬列表的原理與實(shí)現(xiàn)詳解的文章就介紹到這了,更多相關(guān)Vue虛擬列表內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
vue實(shí)現(xiàn)列表無縫滾動(dòng)效果
這篇文章主要為大家詳細(xì)介紹了vue實(shí)現(xiàn)列表無縫滾動(dòng)效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-04-04
Vue3?UI組件庫Element?Plus實(shí)例詳解
這篇文章主要介紹了ElementPlus框架中的一些常用組件,通過示例代碼展示了這些組件的具體使用方法,本文結(jié)合示例代碼給大家介紹的非常詳細(xì),感興趣的朋友一起看看吧2026-05-05
VueX學(xué)習(xí)之modules和namespacedVueX詳細(xì)教程
這篇文章主要為大家介紹了VueX學(xué)習(xí)之modules和namespacedVueX詳細(xì)教程,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-06-06
vue3使用vue-router及路由權(quán)限攔截方式
這篇文章主要介紹了vue3使用vue-router及路由權(quán)限攔截方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-04-04
vue項(xiàng)目中掃碼支付的實(shí)現(xiàn)示例(附demo)
本文主要介紹了vue項(xiàng)目中掃碼支付的實(shí)現(xiàn)示例,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-09-09
element ui表格實(shí)現(xiàn)下拉篩選功能
這篇文章主要為大家詳細(xì)介紹了element ui表格實(shí)現(xiàn)下拉篩選功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-11-11

