Vue3列表触底请求(上手体验hooks新特性)

今天我们来聊一聊业务开发中的触底请求,其实就是分页的一种,只不过传统的分页感觉很丑而已,正好我的小博客最近在做触底分页,借此机会来说一说具体怎么实现的,以及来带领大家使用一下Vue3中的新特性hooks函数。

案例和我实际开发的功能会有差异,这里我只是想讲明白具体的思路。

一、触底分页思路

我这里就列举我自己实现的这种方式把,就是通过监听scroll,通过对比滚动卷入的高度(scrollTop)、窗口的高度(clientHeight)和滚动条的总高度(scrollHeight)三者之间插值的计算,来判断是否发起请求。

后续你可能还会判断:如果数据库没有更多的数据我们就不再请求等操作(这里我就不过多讲解了)。

二、Hooks函数

在 Vue 3 中 ,引入了 Composition API,它是一种新的 API 设计范式,为我们提供了更加灵活和可组合的代码组织方式。使我们的代码写起来更加的舒服,我们今天就来使用一下其中的一个重要概念Hooks

首先Hooks 是一种函数,以 use 开头,用于封装可复用的逻辑。它们提供了一种在函数组件中复用状态逻辑的方式,使我们能够更好地组织组件代码,将相关的逻辑聚合在一起,实现更高水平的可维护性。

借用知乎大佬的定义:集成定义一些可复用的方法,可以随时被引入和调用以实现高内聚低耦合的目标,应该都能算是hook。

下面我们就结合Hook函数,一起来实现一下触底功能。

三、Hooks具体实现

大家会学到两部分,触底的实现思路和hooks的使用。

3.1 创建文件

src 目录下创建 hooks/usePagination.js ,抛出一个函数,我们的所有业务都放在此函数中,并且通过 return 进行返回,我们会使用到一些 Vue钩子(ref, onMounted等),通过 import 进行导入。

js 复制代码
import {ref, onMounted, onUnmounted } from 'vue'

export const usePagination = () => {

    return {
        ...
    }
}

3.2 编写触底业务函数

这里 我用到了三个自己定义的变量loadingcurrentPagelist

  • loading:作用类似于节流,防止我们重复触底,重复请求;
  • currentPage :就是当前页码(默认为1);
  • list :来存储我们http请求的数据。

我们通过setTimeout来模拟一下http请求。

js 复制代码
const loading = ref(false)
const loading = ref(false)
const list = ref([])

const scrollBottom = () => {
    // 判断是否在请求阶段
    if(loading.value) return
    // 获取滚动条卷入的高度
    let scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
    // 获取可视区的高度
    let windowHeight = document.documentElement.clientHeight || document.body.clientHeight;
    // 获取滚动条的总高度
    let scrollHeight = document.documentElement.scrollHeight || document.body.scrollHeight;
    
    if (scrollTop + windowHeight >= scrollHeight) {
        console.log('开始请求');
        loading.value = true;
        currentPage.value ++
        setTimeout(() => {
            loading.value = false
            // http请求函数
            getList(currentPage.value)
        }, 2000)   
    }
}

3.3 http请求函数

请求函数接收两个参数 ,当前页和每次请求的条数,这个我通过for循环pushlist中来模拟。

js 复制代码
const getList = (currentPage = 1, currentSize = 10) => {
    for (let i = 0; i < currentSize; i ++) {
        list.value.push({
            id: new Date().getTime(),
        })
    }
    console.log(list.value)
}

3.4 挂载触底函数

当我们组件加载/注销完成之后挂载/卸载我们的触底函数。

js 复制代码
onMounted(() => {
    console.log('事件已挂载');
    window.addEventListener('scroll', scrollBottom);
    getList();
})

onUnmounted(() => {
    console.log('事件已移除');
    window.removeEventListener('scroll', scrollBottom);
})

3.5 return数据

返回我们在组件中需要用到的变量函数

js 复制代码
export const usePagination = () => {
    ...
    ...
    
    return {
        currentPage,
        list,
        getList
    }
}

3.6 完整代码

js 复制代码
import { ref, onMounted, onUnmounted } from 'vue'  

export const usePagination = () => {
    const currentPage = ref(1)
    const list = ref([])
    const loading = ref(false)
    // 判断触底
    const scrollBottom = () => {
        if(loading.value) return
        let scrollTop = document.documentElement.scrollTop || document.body.scrollTop

        // 变量 windowHeight 是可视区的高度
        let windowHeight =
document.documentElement.clientHeight || document.body.clientHeight
        // 变量 scrollHeight 是滚动条的总高度
        let scrollHeight =
document.documentElement.scrollHeight || document.body.scrollHeight  

        if (scrollTop + windowHeight >= scrollHeight) {
            console.log('开始请求');
            loading.value = true
            currentPage.value ++
            setTimeout(() => {
                loading.value = false
                getList(currentPage.value)
            }, 2000)
        }
    }  

    // 请求数据
    const getList = (currentPage = 1, currentSize = 10) => {
        console.log(currentPage)
        for (let i = 0; i < currentSize; i ++) {
            list.value.push({
                id: new Date().getTime(),
            })
        }
    }

    onMounted(() => {
        console.log('事件已挂载');
        window.addEventListener('scroll', scrollBottom)
        getList()
    })

    onUnmounted(() => {
        console.log('事件已移除');
        window.removeEventListener('scroll', scrollBottom)
    })

    return {
        currentPage,
        list,
        getList
    }
}

四、组件使用

4.1 编写组件模板

我们编写一个列表来渲染我们的list数组

js 复制代码
<template>
    <div class="container">
        <div class="item" v-for="(item, index) in list" :key="item.id">
            我是列表内容{{ index + 1 }}
        </div>
    </div>
</template>

<style lang="scss" scoped>
.container {
    display: flex;
    flex-direction: column;
    align-items: center;
    
    .item {
        width: 60%;
        height: 150px;
        border: 1px solid #000;
        margin-bottom: 10px;
    }
}
</style>

4.1 引入

通过import导入hook函数,执行hook函数,解构出我们需要的值,如果对解构不太了解的小伙伴,可以看我往期的文章,勇宝趣学JavaScript ES6第二章(解构赋值)

js 复制代码
<script setup>
import { usePagination } from '@/hooks/usePagination';

const { currentPage, list } = usePagination();

</script>

五、效果

效果图晚点时间给小伙伴们呈现,在外边,这台笔记本没有做gif的工具,先放一个静态的图片给大家泄泄火把

六、总结

今天给大家讲解了一下触底的实现过程以及 Vue3 中hooks的上手体验,希望大家会对 Vue3 有一个更加深刻的认识。

说实话我写不出那些高大上的代码,哈哈哈,希望大家能理解我这个前端小菜鸟。

相关推荐
ct9785 小时前
组件间的通信
前端·javascript·vue.js
左手吻左脸。5 小时前
Vue 全栈面试题大全(2026 最新版最详细)
前端·javascript·vue.js
Aphasia3115 小时前
手写KeepAlive组件
前端·react.js·面试
两个西柚呀5 小时前
js中的同步和异步,三种处理异步任务的方式
前端·javascript
pe7er6 小时前
软件设计不要“既要又要”
前端·后端·架构
kyriewen6 小时前
从Webpack到Vite:我们迁移了一个10万行代码的项目,总结了这7个坑
前端·webpack·vite
IT_陈寒6 小时前
Java Stream并行流的坑:我花了3小时才找到的线程安全问题
前端·人工智能·后端
小新1106 小时前
最简单但完整的 Vue 响应式示例(一个简单的计数器按钮)
前端·javascript·vue.js
川冰ICE6 小时前
JavaScript进阶④|Symbol与元编程,对象的隐藏身份
开发语言·javascript·ecmascript
水煮白菜王6 小时前
开源 AI 桌宠 Clawd on Desk:让 Claude Code 的状态从终端‘蹦‘到桌面
javascript·人工智能·开源