Vue3实现列表无限滚动的示例详解

 更新时间:2023年07月16日 16:39:53   作者:一只大加号  
这篇文章主要为大家详细介绍了如何使用Vue3实现列表无限滚动的效果,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起了解一下

先看成果

无限滚动列表

无限滚动列表(Infinite Scroll)是一种在网页或应用程序中加载和显示大量数据的技术。它通过在用户滚动到页面底部时动态加载更多内容,实现无缝的滚动体验,避免一次性加载所有数据而导致性能问题。供更流畅的用户体验。但需要注意在实现时,要考虑合适的加载阈值、数据加载的顺序和流畅度,以及处理加载错误或无更多数据的情况,下面我们用IntersectionObserver来实现无线滚动,并且在vue3+ts中封装成一个可用的hook。

IntersectionObserver是什么

IntersectionObserver(交叉观察器)是一个Web API,用于有效地跟踪网页中元素在视口中的可见性。它提供了一种异步观察目标元素与祖先元素或视口之间交叉区域变化的方式。 IntersectionObserver的主要目的是确定一个元素何时进入或离开视口,或者与另一个元素相交。它在各种场景下非常有用,例如延迟加载图片或其他资源,实现无限滚动等。

这里用一个demo来做演示

demo代码如下,其实就是用IntersectionObserver来对某个元素做一个监听,通过siIntersecting属性来判断监听元素的显示和隐藏。

 const observer = new IntersectionObserver((entries) => {
  entries.forEach((entry) => {
    if (entry.isIntersecting) {
        console.log('元素出现');
    } else{
      console.log('元素隐藏');
    }
  });
});
observer.observe(bottom);

无限滚动实现

下面我们开始动手

1.数据模拟

模拟获取数据,比如分页的数据,这里是模拟的表格滚动的数据,每次只加载十条,类似于平时的翻页效果,这里写的比较简单, 在这里给它加了一个最大限度30条,超过30条就不再继续增加了

<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实现

import { createVNode, render, Ref } from 'vue';
/**
 接受一个列表函数、列表容器、底部样式
 */
export function useScroll() {
    // 用ts定义传入的三个参数类型
    async function init(fn:()=>Promise<any[] | unknown>,container:Ref) {
        const res = await fn();
      }
    return { init }
}

执行init就相当于加载了第一次列表 后续通过滚动继续加载列表

import { useScroll } from "../hooks/useScroll.ts";
onMounted(() => {
  const {init} = useScroll()
  //三个参数分别是 加载分页的函数 放置数据的容器 结尾的提示dom
  init(getList,container,bottom)
});

3.监听元素

export function useScroll() {
    // 用ts定义传入的三个参数类型
    async function init(fn:()=>Promise<any[] | unknown>,container:Ref,bottom?:HTMLDivElement) {
        const res = await fn();
        // 使用IntersectionObserver来监听bottom的出现
        const observer = new IntersectionObserver((entries) => {
          entries.forEach((entry) => {
            if (entry.isIntersecting) {
                fn();
                console.log('元素出现');
            } else{
              console.log('元素隐藏');
            }
          });
        });
        observer.observe(bottom);
      }
    return { init }
}

4.hook初始化

获取需要做无限滚动的容器 这里我们用ref的方式来直接获取到dom节点 大家也可以尝试下用getCurrentInstance这个api来获取到

整个实例,其实就是类似于vue2中的this.$refs.container来获取到dom节点容器

根据生命周期我们知道dom节点是在mounted中再挂载的,所以想要拿到dom节点,要在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来生成虚拟节点 然后挂载到容器container中
  const {init} = useScroll()
  //三个参数分别是 加载分页的函数 放置数据的容器 结尾的提示dom
  init(getList,container,bottom)
});

这部分代码是生成放到末尾的dom节点 封装的init方法可以自定义传入末尾的提示dom,也可以不传,封装的方法中有默认的dom

优化功能

自定义默认底部提示dom

async function init(fn:()=>Promise<any[] | unknown>,container:Ref,bottom?:HTMLDivElement) {
        const res = await fn();
        // 如果没有传入自定义的底部dom 那么就生成一个默认底部节点 
        if(!bottom){
          const vnode = createVNode('div', { id: 'bottom',style:"color:#000" }, '已经到底啦~');
          render(vnode, container.value!);
          bottom = document.getElementById('bottom') as HTMLDivElement;
        }
        // 使用IntersectionObserver来监听bottom的出现
        const observer = new IntersectionObserver((entries) => {
          entries.forEach((entry) => {
            if (entry.isIntersecting) {
                fn();
                console.log('元素出现');
            } else{
              console.log('元素隐藏');
            }
          });
        });
        observer.observe(bottom);
      }

完整代码

import { createVNode, render, Ref } from 'vue';
/**
 接受一个列表函数、列表容器、底部样式
 */
export function useScroll() {
    async function init(fn:()=>Promise<any[] | unknown>,container:Ref,bottom?:HTMLDivElement) {
        const res = await fn();
        // 生成一个默认底部节点
        if(!bottom){
          const vnode = createVNode('div', { id: 'bottom' }, '已经到底啦~');
          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实现列表无限滚动的示例详解的文章就介绍到这了,更多相关Vue3列表无限滚动内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 手把手带你安装vue-cli并创建第一个vue-cli应用程序

    手把手带你安装vue-cli并创建第一个vue-cli应用程序

    vue-cli这个构建工具大大降低了webpack的使用难度,支持热更新,有webpack-dev-server的支持,相当于启动了一个请求服务器,给你搭建了一个测试环境,下面这篇文章主要给大家介绍了关于安装vue-cli并创建第一个vue-cli应用程序的相关资料,需要的朋友可以参考下
    2022-08-08
  • 解决vux 中popup 组件Mask 遮罩在最上层的问题

    解决vux 中popup 组件Mask 遮罩在最上层的问题

    这篇文章主要介绍了解决vux 中popup 组件Mask 遮罩在最上层的问题,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-11-11
  • Vue3+TypeScript项目中安装PDF.js详细的步骤

    Vue3+TypeScript项目中安装PDF.js详细的步骤

    这篇文章主要介绍了Vue3+TypeScript项目中安装PDF.js详细的步骤,通过示例代码详细介绍了包括安装步骤、基础使用示例、创建可复用PDF查看器组件、注意事项等,需要的朋友可以参考下
    2026-01-01
  • 利用v-viewer图片预览插件放大需要预览的图片

    利用v-viewer图片预览插件放大需要预览的图片

    本文介绍了v-viewer插件的安装和使用步骤,包括npm安装、在main.js文件中全局引入,以及常用的三种使用方式,文章提供了简单的布局页面效果,文中通过代码介绍的非常详细,需要的朋友可以参考下
    2024-10-10
  • Vue中的计算属性computed传参方式

    Vue中的计算属性computed传参方式

    这篇文章主要介绍了Vue中的计算属性computed传参方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2023-11-11
  • vue组件props不同数据类型传参的默认值问题

    vue组件props不同数据类型传参的默认值问题

    这篇文章主要介绍了vue组件props不同数据类型传参的默认值问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-07-07
  • Vue-cli配置打包文件本地使用的教程图解

    Vue-cli配置打包文件本地使用的教程图解

    这篇文章主要介绍了Vue-cli配置打包文件本地使用的教程图解,非常不错,具有一定的参考借鉴价值,需要的朋友可以参考下
    2018-08-08
  • Vue+canvas实现视频截图功能

    Vue+canvas实现视频截图功能

    这篇文章主要为大家详细介绍了Vue+canvas实现视频截图功能,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-07-07
  • vue this.$toast 失效问题解决方案

    vue this.$toast 失效问题解决方案

    这篇文章主要介绍了vue this.$toast 失效问题汇总,本文给大家分享完美解决方案,感兴趣的朋友跟随小编一起看看吧
    2024-03-03
  • 使用sessionStorage解决vuex在页面刷新后数据被清除的问题

    使用sessionStorage解决vuex在页面刷新后数据被清除的问题

    localStorage没有时间期限,除非将它移除,sessionStorage即会话,当浏览器关闭时会话结束,有时间期限,具有自行百度。本文使用的是sessionStorage解决vuex在页面刷新后数据被清除的问题,需要的朋友可以参考下
    2018-04-04

最新评论