Vue中虛擬列表的原理與實現(xiàn)詳解
前言
最近在工作中遇到了一個列表的需求,因為做的是C端,所以對性能體驗要求很高,主要有下面幾點要求,也可以說是痛點
- 就算加載上千條數(shù)據(jù),也能夠立即加載,而不會卡頓,等待時間不能太長
- 加載好以后用戶能立馬進行交互,而不是要等待,體驗必須絲滑
仔細想想, 加載上千條數(shù)據(jù),加載時間肯定會相對長一些,而又要求立即加載,這不是一個矛盾的需求么,所以如果使用常規(guī)的操作,肯定難以解決這個痛點,所以必須引出我們今天的主角才能解決今天的問題
什么是虛擬列表
虛擬列表是一種技術(shù),它只渲染用戶當前可見的列表項,而不是渲染整個列表。
虛擬列表的原理是什么
只渲染可視區(qū)域內(nèi)的列表項,而不是渲染整個列表
當用戶滾動容器時,虛擬列表會根據(jù)滾動位置和可視區(qū)域的大小計算出當前應該顯示的列表項。
首先實現(xiàn)一個可以滑動的列表
因為我們主要講的是虛擬列表,為了不增加大家負擔,那么列表容器的高度,列表項的高度,我們都設(shè)置為已知的,而實際使用的時候,可以根據(jù)場景,動態(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>
測試結(jié)果非??植?,因為數(shù)據(jù)量龐大,發(fā)現(xiàn)才開始的時候一直卡著不動,有1s多的時間是什么也看不到的,這無疑是致命的
實現(xiàn)虛擬列表
列表高度為300,列表項高度為50,那么我們只需要展示6條數(shù)據(jù)就可以了,因為有可能上拉數(shù)據(jù),那么有可能展示第7條數(shù)據(jù),所以我們要展示7條數(shù)據(jù),但是首次加載我們只用展示6條
所以此時我們改一下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ū)域的列表,那么如何顯示滾動條呢
這個問題就是一個關(guān)鍵,需要我們設(shè)置一個虛擬的列表,定義好高度
<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)在我們的效果是這樣的,下拉的時候,列表內(nèi)容都上移了
所以我們要讓列表內(nèi)容在滾動的時候,移動到可視區(qū)域內(nèi)
我們監(jiān)聽scrollView的 scroll事件
<div class="scrollView" @scroll="onScroll" ref="list">
onScroll的實現(xiàn)包括以下幾個內(nèi)容
- 滾動距離:
scrollTop - 列表開始索引:
startIndex - 列表結(jié)束索引:
endIndex - 列表移動距離:
startOffset: 這個是重點
this.startOffset = scrollTop - (scrollTop % this.itemSize);
所以我們的onScroll方法的實現(xiàn)是
onScroll() {
//當前滾動位置
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);
// 列表移動距離
this.startOffset = scrollTop - (scrollTop % this.itemSize);
}此時我們的虛擬列表就實現(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() {
//當前滾動位置
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);
// 列表移動距離
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ū)別。
虛擬列表通過動態(tài)渲染當前可見的列表項來提高性能和內(nèi)存利用率,而下拉加載更多是在用戶滾動到列表底部時自動加載更多數(shù)據(jù)。兩者都是為了優(yōu)化大數(shù)據(jù)集或長列表的用戶界面,提供更好的性能和用戶體驗。具體選擇哪種技術(shù)取決于具體的需求和場景。
參考
「前端進階」高性能渲染十萬條數(shù)據(jù)(虛擬列表)
到此這篇關(guān)于Vue中虛擬列表的原理與實現(xiàn)詳解的文章就介紹到這了,更多相關(guān)Vue虛擬列表內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
VueX學習之modules和namespacedVueX詳細教程
這篇文章主要為大家介紹了VueX學習之modules和namespacedVueX詳細教程,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-06-06
vue3使用vue-router及路由權(quán)限攔截方式
這篇文章主要介紹了vue3使用vue-router及路由權(quán)限攔截方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-04-04

