Vue3實現(xiàn)列表無限滾動的示例詳解
先看成果

無限滾動列表
無限滾動列表(Infinite Scroll)是一種在網(wǎng)頁或應用程序中加載和顯示大量數(shù)據(jù)的技術。它通過在用戶滾動到頁面底部時動態(tài)加載更多內(nèi)容,實現(xiàn)無縫的滾動體驗,避免一次性加載所有數(shù)據(jù)而導致性能問題。供更流暢的用戶體驗。但需要注意在實現(xiàn)時,要考慮合適的加載閾值、數(shù)據(jù)加載的順序和流暢度,以及處理加載錯誤或無更多數(shù)據(jù)的情況,下面我們用IntersectionObserver來實現(xiàn)無線滾動,并且在vue3+ts中封裝成一個可用的hook。
IntersectionObserver是什么
IntersectionObserver(交叉觀察器)是一個Web API,用于有效地跟蹤網(wǎng)頁中元素在視口中的可見性。它提供了一種異步觀察目標元素與祖先元素或視口之間交叉區(qū)域變化的方式。 IntersectionObserver的主要目的是確定一個元素何時進入或離開視口,或者與另一個元素相交。它在各種場景下非常有用,例如延遲加載圖片或其他資源,實現(xiàn)無限滾動等。
這里用一個demo來做演示

demo代碼如下,其實就是用IntersectionObserver來對某個元素做一個監(jiān)聽,通過siIntersecting屬性來判斷監(jiān)聽元素的顯示和隱藏。
const observer = new IntersectionObserver((entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
console.log('元素出現(xiàn)');
} else{
console.log('元素隱藏');
}
});
});
observer.observe(bottom);無限滾動實現(xiàn)
下面我們開始動手
1.數(shù)據(jù)模擬
模擬獲取數(shù)據(jù),比如分頁的數(shù)據(jù),這里是模擬的表格滾動的數(shù)據(jù),每次只加載十條,類似于平時的翻頁效果,這里寫的比較簡單, 在這里給它加了一個最大限度30條,超過30條就不再繼續(xù)增加了
<template>
<div ref="container" class="container">
<div v-for="item in list" class="box">{{ item.id }}</div>
</div>
</template>
<script setup lang="ts">
const list: any[] = reactive([]);
let idx = 0;
function getList() {
return new Promise((res) => {
if(idx<30){
for (let i = idx; i < idx + 10; i++) {
list.push({ id: i });
}
idx += 10
}
res(1);
});
</script>2.hook實現(xiàn)
import { createVNode, render, Ref } from 'vue';
/**
接受一個列表函數(shù)、列表容器、底部樣式
*/
export function useScroll() {
// 用ts定義傳入的三個參數(shù)類型
async function init(fn:()=>Promise<any[] | unknown>,container:Ref) {
const res = await fn();
}
return { init }
}執(zhí)行init就相當于加載了第一次列表 后續(xù)通過滾動繼續(xù)加載列表
import { useScroll } from "../hooks/useScroll.ts";
onMounted(() => {
const {init} = useScroll()
//三個參數(shù)分別是 加載分頁的函數(shù) 放置數(shù)據(jù)的容器 結尾的提示dom
init(getList,container,bottom)
});3.監(jiān)聽元素
export function useScroll() {
// 用ts定義傳入的三個參數(shù)類型
async function init(fn:()=>Promise<any[] | unknown>,container:Ref,bottom?:HTMLDivElement) {
const res = await fn();
// 使用IntersectionObserver來監(jiān)聽bottom的出現(xiàn)
const observer = new IntersectionObserver((entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
fn();
console.log('元素出現(xiàn)');
} else{
console.log('元素隱藏');
}
});
});
observer.observe(bottom);
}
return { init }
}4.hook初始化
獲取需要做無限滾動的容器 這里我們用ref的方式來直接獲取到dom節(jié)點 大家也可以嘗試下用getCurrentInstance這個api來獲取到
整個實例,其實就是類似于vue2中的this.$refs.container來獲取到dom節(jié)點容器
根據(jù)生命周期我們知道dom節(jié)點是在mounted中再掛載的,所以想要拿到dom節(jié)點,要在onMounted里面獲取到,畢竟沒掛載肯定是拿不到的嘛
const container = ref<HTMLElement | null>(null);
onMounted(() => {
const vnode = createVNode('div', { id: 'bottom',style:"color:#000" }, '到底了~');
render(vnode, container.value!);
const bottom = document.getElementById('bottom') as HTMLDivElement;
// 用到的是createVNode來生成虛擬節(jié)點 然后掛載到容器container中
const {init} = useScroll()
//三個參數(shù)分別是 加載分頁的函數(shù) 放置數(shù)據(jù)的容器 結尾的提示dom
init(getList,container,bottom)
});這部分代碼是生成放到末尾的dom節(jié)點 封裝的init方法可以自定義傳入末尾的提示dom,也可以不傳,封裝的方法中有默認的dom
優(yōu)化功能
自定義默認底部提示dom
async function init(fn:()=>Promise<any[] | unknown>,container:Ref,bottom?:HTMLDivElement) {
const res = await fn();
// 如果沒有傳入自定義的底部dom 那么就生成一個默認底部節(jié)點
if(!bottom){
const vnode = createVNode('div', { id: 'bottom',style:"color:#000" }, '已經(jīng)到底啦~');
render(vnode, container.value!);
bottom = document.getElementById('bottom') as HTMLDivElement;
}
// 使用IntersectionObserver來監(jiān)聽bottom的出現(xiàn)
const observer = new IntersectionObserver((entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
fn();
console.log('元素出現(xiàn)');
} else{
console.log('元素隱藏');
}
});
});
observer.observe(bottom);
}完整代碼
import { createVNode, render, Ref } from 'vue';
/**
接受一個列表函數(shù)、列表容器、底部樣式
*/
export function useScroll() {
async function init(fn:()=>Promise<any[] | unknown>,container:Ref,bottom?:HTMLDivElement) {
const res = await fn();
// 生成一個默認底部節(jié)點
if(!bottom){
const vnode = createVNode('div', { id: 'bottom' }, '已經(jīng)到底啦~');
render(vnode, container.value!);
bottom = document.getElementById('bottom') as HTMLDivElement;
}
const observer = new IntersectionObserver((entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
fn();
}
});
});
observer.observe(bottom);
}
return { init }
}<template>
<div ref="container" class="container">
<div v-for="item in list" class="box">{{ item.id }}</div>
</div>
</template>
<script setup lang="ts">
import { onMounted, createVNode, render, ref, reactive } from 'vue';
import { useScroll } from "../hooks/useScroll.ts";
const list: any[] = reactive([]);
let idx = 0;
function getList() {
return new Promise((res,rej) => {
if(idx<=30){
for (let i = idx; i < idx + 10; i++) {
list.push({ id: i });
}
idx += 10
res(1);
}
rej(0)
});
}
const container = ref<HTMLElement | null>(null);
onMounted(() => {
const vnode = createVNode('div', { id: 'bottom' }, '到底了~');
render(vnode, container.value!);
const bottom = document.getElementById('bottom') as HTMLDivElement;
const {init} = useScroll()
init(getList,container,bottom)
});
</script>
<style scoped>
.container {
border: 1px solid black;
width: 200px;
height: 100px;
overflow: overlay
}
.box {
height: 30px;
width: 100px;
background: red;
margin-bottom: 10px
}</style>到此這篇關于Vue3實現(xiàn)列表無限滾動的示例詳解的文章就介紹到這了,更多相關Vue3列表無限滾動內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
Vue中ElementUI結合transform使用時如何修復el-select彈框定位不準確問題
這篇文章主要介紹了Vue中ElementUI結合transform使用時如何修復el-select彈框定位不準確問題,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友參考下吧2024-01-01
vue select選擇框數(shù)據(jù)變化監(jiān)聽方法
今天小編就為大家分享一篇vue select選擇框數(shù)據(jù)變化監(jiān)聽方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-08-08
解決vue創(chuàng)建項目使用vue-router和vuex報錯Object(...)is not a&nb
這篇文章主要介紹了解決vue創(chuàng)建項目使用vue-router和vuex報錯Object(...)is not a function問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-02-02
Vue Element前端應用開發(fā)之開發(fā)環(huán)境的準備工作
這篇文章主要介紹了Vue Element前端應用開發(fā)之開發(fā)環(huán)境的準備工作,對Vue感興趣的同學,可以來學習一下2021-05-05
Vue+element+cookie記住密碼功能的簡單實現(xiàn)方法
這篇文章主要給大家介紹了Vue+element+cookie記住密碼功能的簡單實現(xiàn)方法,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2020-09-09

