詳解Vue如何手寫一個虛擬列表
前言
何為虛擬列表?
回答這個問題先想一下,對于返回的幾千上萬條列表數(shù)據(jù),作為前端會如何渲染?
分頁唄,最開始想到的就是分頁。但是,萬一接口沒做分頁呢?好,就算后端做分頁了,那么用戶在瀏覽到第幾千條、第幾萬條數(shù)據(jù)時前端難道要把這第幾千條、幾萬條的DOM都渲染出來嗎?顯然是很消耗性能的(畢竟如此多的DOM,而且每個DOM內(nèi)部都有其他樣式細(xì)節(jié))如何優(yōu)化?此時就可以用到虛擬列表了
虛擬列表是一種優(yōu)化長列表渲染的技術(shù),它可以在保持流暢性的同時,渲染大量的數(shù)據(jù)。
在傳統(tǒng)的列表渲染中,如果列表非常長,會導(dǎo)致渲染時間過長(前面所說的會有幾千幾萬個DOM),頁面卡頓,用戶體驗變得非常差。而虛擬列表則是只渲染可見區(qū)域內(nèi)的數(shù)據(jù),而非全部渲染,這樣就可以大大提高渲染效率,保持頁面流暢性
常見場景
商品列表、社交列表...(暫時想到這兩個)
手寫虛擬列表
進行虛擬列表的實現(xiàn)之前先搞清楚一個問題:
是什么造成了這么多數(shù)據(jù)在渲染時產(chǎn)生卡頓?是數(shù)據(jù)數(shù)量太多引起的嗎?
準(zhǔn)確來說,應(yīng)該是是由于要渲染的DOM
太多造成的卡頓。數(shù)據(jù)本身只是數(shù)據(jù),對于拿到的幾千上萬條數(shù)據(jù)它本身的大小對于內(nèi)存來說只能說是冰山一角吧
虛擬列表的原理
數(shù)據(jù)我還是這么多數(shù)據(jù),但是我不一次性渲染這么多數(shù)據(jù),我只渲染其中的一小部分(比如十條),這樣當(dāng)用戶滾動的時候就重復(fù)變化這一小部分的DOM
的渲染效果。這樣就能從原先幾千上萬個DOM
變成現(xiàn)在的十個,減少了一個量級,減輕了渲染的壓力。而這一小部分顯示的區(qū)域暫稱之為視圖區(qū)域吧
審查元素,大致的效果如圖:
賊長的這部分是所有的數(shù)據(jù)的盒子所占的大小,但是每次只控制小部分?jǐn)?shù)據(jù)在上面的盒子進行顯示
虛擬列表的實現(xiàn)
怎么實現(xiàn)上面的效果?這里用到了固定定位和絕對定位
<script lang='ts' setup> type Item = { id: number name: string } const allListData = ref<Item[]>([]) // 存放十萬條數(shù)據(jù) const itemHeight = ref(40) // 每一條(項)的高度 const count = ref(10) // 一屏展示幾條數(shù)據(jù) const startIndex = ref(0) // 開始位置的索引 const endIndex = ref(10) // 結(jié)束位置的索引 const topVal = ref(0) // 父元素滾動位置 // 計算展示的列表 const showListData = computed(() => allListData.value.slice(startIndex.value, endIndex.value)) // 模擬十萬條數(shù)據(jù) const getData = async () => { for (let i = 0; i < 10000; i++) { allListData.value.push({ name: `第${i}條數(shù)據(jù)`, id: i }) } } // 初始化加載 onMounted(() => { getData() }) // 虛擬列表視口區(qū)域的組件實例 const viewport = ref<HTMLDivElement>() const handleScroll = () => { console.log('滾動了') // 非空判斷 if (!viewport.value) return // 獲取滾動距離(這里通過組件實例獲取的,當(dāng)然也可以通過在該事件的事件參數(shù)中拿到) const scrollTop = viewport.value.scrollTop // 計算起始下標(biāo)和結(jié)束下標(biāo),用于 computed 計算 startIndex.value = Math.floor(scrollTop / itemHeight.value) endIndex.value = startIndex.value + count.value // 動態(tài)更改定位的 top 值,動態(tài)展示相應(yīng)內(nèi)容 topVal.value = viewport.value.scrollTop } </script> <template> <!-- 虛擬列表容器 --> <div class="viewport" ref="viewport" :style="{ height: 10條數(shù)據(jù)撐開的高度 }" > <!-- 占位元素,高度為所有的數(shù)據(jù)的總高度 --> <div class="placeholder" :style="{ height:itemHeight * count + 'px' }" ></div> <!-- 視圖區(qū),展示10條數(shù)據(jù),注意其定位的top值是變化的 --> <div class="list" :style="{ top: topVal + 'px' }"> <!-- 每一條(項)數(shù)據(jù) --> <div v-for="item in showListData" :key="item.id" class="item" :style="{ height: itemHeight + 'px' }" > {{ item.name }} </div> </div> </div> </template> <style scoped lang="scss"> .viewport { box-sizing: border-box; width: 240px; border: solid 1px #000000; // 開啟滾動條 overflow-y: auto; // 開啟相對定位 position: relative; .list { width: 100%; height: auto; // 搭配使用絕對定位 position: absolute; top: 0; left: 0; .item { box-sizing: border-box; width: 100%; height: 40px; line-height: 40px; text-align: center; // 隔行變色 &:nth-child(even) { background: skyblue; } &:nth-child(odd) { background: #fff; } } } } </style>
一開始視圖區(qū)域的top
值為0
,剛好在最頂端,監(jiān)聽滾動事件,當(dāng)滾動時實時改變top
值以及該區(qū)域內(nèi)渲染的數(shù)據(jù),從而實現(xiàn)了虛擬列表
視圖區(qū)域的top
值為什么要動態(tài)監(jiān)聽?試想一下,現(xiàn)在所有數(shù)據(jù)的總高度為1000px
,視圖區(qū)域為100px,當(dāng)用戶滾動到500px
時,如果視圖區(qū)域的top
值不動態(tài)綁定,那么視圖區(qū)域還停留在top=0
(也就是最頂端處),那肯定就看不到最新的視圖了
到此這篇關(guān)于詳解Vue如何手寫一個虛擬列表的文章就介紹到這了,更多相關(guān)Vue虛擬列表內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
elementUI表格多選框this.$refs.xxx.toggleRowSelection無效問題
這篇文章主要介紹了elementUI表格多選框this.$refs.xxx.toggleRowSelection無效問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2023-11-11Vue中 v-if/v-show/插值表達式導(dǎo)致閃現(xiàn)的原因及解決辦法
在開發(fā)過程中經(jīng)常會發(fā)現(xiàn)當(dāng)頁面明明不應(yīng)該出現(xiàn)的元素或內(nèi)容會閃現(xiàn)一下然后消失,這篇文章給大家分享Vue中 v-if/v-show/插值表達式導(dǎo)致閃現(xiàn)的原因及解決辦法,一起看看吧2018-10-10vue+koa2實現(xiàn)session、token登陸狀態(tài)驗證的示例
這篇文章主要介紹了vue+koa2實現(xiàn)session、token登陸狀態(tài)驗證的示例,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-08-08基于vue實現(xiàn)多引擎搜索及關(guān)鍵字提示
這篇文章主要為大家詳細(xì)介紹了基于vue實現(xiàn)多引擎搜索及關(guān)鍵字提示的相關(guān)資料,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-03-03