基于Vue3實現(xiàn)列表虛擬滾動效果
前言
近期在做一個網(wǎng)頁播放器項目中,用到很多需要展示歌單的列表
一個歌單動輒千百首歌曲,頁面中的元素太多導(dǎo)致熱重載的時候 chrome 直接崩了 ??
于是無限滾動列表提上日程
寫的有點亂,也是第一次用 typescript 寫項目,先記錄一下
完成效果

思路和需要解決的問題
與懶加載不同,虛擬滾動需要一次性獲取所有數(shù)據(jù),但是只顯示屏幕可見范圍內(nèi)的數(shù)據(jù)
要做到這些我需要知道:
- 一行的高度
- 屏幕范圍內(nèi)能顯示的行數(shù)
- 列表在頁面中距離網(wǎng)頁頂部的位置
- 滾動條高度
假設(shè)滿屏能容納 10 條數(shù)據(jù),需要加載的數(shù)據(jù)是一個數(shù)組listData,只需要裁剪數(shù)據(jù)范圍listData.slice(0, 10)
隨著滾動條向下,將滾動條高度/一行的高度可以計算出當(dāng)前行數(shù)
而要模擬滾動條高度就要在頁面掛載時就手動設(shè)置頁面的高度一行高度*listData.length
最后也是最關(guān)鍵的是保持列表一直保持在當(dāng)前位置上,手動設(shè)置列表容器padding-top等于當(dāng)前滾動條高度
有一個仍未解決的問題,就是每次來回滾動歌曲封面都要重新請求 ??
vue3+setup 寫的組件
<script lang="ts" setup>
import { ref, computed, nextTick, reactive, watchEffect, onUnmounted } from 'vue'
const props = defineProps<{
listData: Array<any>
}>()
// 列表HTMLElementDom
const ulRef = ref<any>(null)
// 屏幕高度
const screenH = document.documentElement.clientHeight
const data = reactive<any>({
// 列表第一項的高度(起始高度)
initH: 0,
// 一行的高度
unitH: 0,
// 屏幕范圍內(nèi)能顯示個數(shù)
displayCount: 1,
// 列表起始值
startIdx: 0
})
const listData = computed(() => {
let endIdx = data.startIdx + data.displayCount
if (endIdx >= props.listData.length) endIdx = props.listData.length
return props.listData.slice(data.startIdx, endIdx).map((v, k) => {
v.idx = data.startIdx + k + 1
return v
})
})
function scrollHandler() {
// 當(dāng)前滾動高度
const curScrollTop = document.documentElement.scrollTop
if (curScrollTop > data.initH) {
const addCount = Math.floor((curScrollTop - data.initH) / data.unitH)
ulRef.value.style.setProperty('padding-top', `${addCount * data.unitH}px`)
data.startIdx = addCount
} else {
ulRef.value.style.setProperty('padding-top', '0px')
data.startIdx = 0
}
}
watchEffect(() => {
if (props.listData.length > 0) {
nextTick(() => {
// 列表距離頂部距離
data.initH = ulRef.value.getBoundingClientRect().top + document.documentElement.scrollTop
// 計算每行高度
data.unitH = ulRef.value.children[0].offsetHeight
// 計算屏幕內(nèi)能顯示的行數(shù)
data.displayCount = Math.ceil(screenH / data.unitH)
// 設(shè)置列表總高度 = 一行高度 * 行數(shù)
const listH = data.unitH * props.listData.length
ulRef.value.style.setProperty('height', `${listH}px`)
window.removeEventListener('scroll', scrollHandler)
window.addEventListener('scroll', scrollHandler)
})
}
})
onUnmounted(() => {
window.removeEventListener('scroll', scrollHandler)
})
</script>
<template>
<ul ref="ulRef">
<li v-for="(listItem, listIndex) in listData" :key="`list-${listIndex}`" :data-idx="listItem.idx">
<slot :listItem="listItem"></slot>
</li>
</ul>
</template>使用組件
<script lang="ts" setup>
import InfiniteList from './InfiniteList.vue'
const songs = [] // 列表數(shù)據(jù)
</script>
<template>
<infinite-list :listData="songs">
<template #default="{ listItem }">
<div>{{ listItem.title }}</div>
<!-- ... -->
</template>
</infinite-list>
</template>到此這篇關(guān)于基于Vue3實現(xiàn)列表虛擬滾動效果的文章就介紹到這了,更多相關(guān)Vue列表虛擬滾動內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
使用Element+vue實現(xiàn)開始與結(jié)束時間限制
這篇文章主要為大家詳細(xì)介紹了使用Element+vue實現(xiàn)開始與結(jié)束時間限制,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2021-08-08
解決vue props傳Array/Object類型值,子組件報錯的情況
這篇文章主要介紹了解決vue props傳Array/Object類型值,子組件報錯的情況,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-11-11
vue-cli3在main.js中console.log()會報錯的解決
這篇文章主要介紹了vue-cli3在main.js中console.log()會報錯的解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-04-04
vue.js通過自定義指令實現(xiàn)數(shù)據(jù)拉取更新的實現(xiàn)方法
數(shù)據(jù)拉取更新這個功能相信大家基本都見過,但是如果要做起來卻不止如何做起,所以這篇文章給大家分享了vue.js通過自定義指令實現(xiàn)的方法,閱讀本文需要對vue有一定理解,有需要的朋友們下面來一起看看吧。2016-10-10
通過vue-cli3構(gòu)建一個SSR應(yīng)用程序的方法
這篇文章主要介紹了通過vue-cli3構(gòu)建一個SSR應(yīng)用程序,以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。2018-09-09

