Vue中虛擬列表的原理與實(shí)現(xiàn)詳解
前言
最近在工作中遇到了一個(gè)列表的需求,因?yàn)樽龅氖荂端,所以對性能體驗(yàn)要求很高,主要有下面幾點(diǎn)要求,也可以說是痛點(diǎn)
- 就算加載上千條數(shù)據(jù),也能夠立即加載,而不會(huì)卡頓,等待時(shí)間不能太長
- 加載好以后用戶能立馬進(jìn)行交互,而不是要等待,體驗(yàn)必須絲滑
仔細(xì)想想, 加載上千條數(shù)據(jù),加載時(shí)間肯定會(huì)相對長一些,而又要求立即加載,這不是一個(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>
測試結(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)鍵,需要我們設(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)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
@click.native和@click的區(qū)別及說明
這篇文章主要介紹了@click.native和@click的區(qū)別及說明,具有很好的參考價(jià)值,希望對大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-08-08vue3+Element Plus實(shí)現(xiàn)自定義穿梭框的詳細(xì)代碼
找到一個(gè)好用的vue樹形穿梭框組件都很難,又不想僅僅因?yàn)橐粋€(gè)穿梭框在element-ui之外其他重量級插件,本文給大家分享vue3+Element Plus實(shí)現(xiàn)自定義穿梭框的示例代碼,感興趣的朋友一起看看吧2024-01-01Vue中ElementUI結(jié)合transform使用時(shí)彈框定位不準(zhǔn)確問題解析
在近期開發(fā)中,需要將1920*1080放到更大像素大屏上演示,所以需要使用到transform來對頁面進(jìn)行縮放,但是此時(shí)發(fā)現(xiàn)彈框定位出錯(cuò)問題,無法準(zhǔn)備定位到實(shí)際位置,本文給大家分享Vue中ElementUI結(jié)合transform使用時(shí)彈框定位不準(zhǔn)確解決方法,感興趣的朋友一起看看吧2024-01-01詳解Vue調(diào)用手機(jī)相機(jī)和相冊以及上傳
這篇文章主要介紹了Vue調(diào)用手機(jī)相機(jī)及上傳,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-05-05element-ui?tree?異步樹實(shí)現(xiàn)勾選自動(dòng)展開、指定展開、指定勾選功能
這篇文章主要介紹了element-ui?tree?異步樹實(shí)現(xiàn)勾選自動(dòng)展開、指定展開、指定勾選,項(xiàng)目中用到了vue的element-ui框架,用到了el-tree組件,由于數(shù)據(jù)量很大,使用了數(shù)據(jù)懶加載模式,即異步樹,需要的朋友可以參考下2022-08-08