【前端】Vue项目:旅游App-(16)home+hooks:窗口滚动到底部动态加载新数据、抽取到hook

news2024/9/28 19:03:47

文章目录

    • 目标
    • 过程与代码
      • 监听窗口的滚动
      • 窗口上事件监听的移除
      • 封装到一个hook
        • 回调函数法(不推荐)
        • 返回值法(推荐)
    • 效果
    • 总代码
      • 修改或添加的文件
      • hooks的useScroll
      • home-content
    • 参考

本项目博客总结:【前端】Vue项目:旅游App-博客总结

目标

监听窗口的滚动,滚动到底部则动态地加载houseList数据。
在这里插入图片描述

过程与代码

监听窗口的滚动

首先要监听窗口的滚动。

注意,滚动有两种:窗口滚动和页面滚动,关于如何区分它们:【前端】如何判断是页面滚动还是窗口滚动

window.addEventListener('scroll', () => {
    // 当前位置到顶部的距离
    const scrollTop = document.documentElement.scrollTop
    // 屏幕的长度
    const clientHeight = document.documentElement.clientHeight
    // 页面总体长度
    const scrollHeight = document.documentElement.scrollHeight

    // 滚动到底部:提前一点刷新
    if (scrollHeight <= scrollTop + clientHeight + 1) {
        homeStore.fetchHouseList()
        console.log('滚动到底部')
    }
})

效果:滚动到底部后就加载出了新的数据。

在这里插入图片描述

窗口上事件监听的移除

写到这里我们要进行一些思考:滚动到底部就加载数据 这一事件是绑定到整个App的窗口上的,但我们只需要它在home页面上绑定。

当我们切换到其他页面,如favor页面时,我们需要remove这个事件。也就是说,我们在写这个代码的时候要考虑到生命周期

生命周期选项 | Vue.js (vuejs.org)

生命周期钩子 | Vue.js (vuejs.org)

unmounted时移除此事件:

const scrollBottomListener = () => {
    // 当前位置到顶部的距离
    const scrollTop = document.documentElement.scrollTop
    // 屏幕的长度
    const clientHeight = document.documentElement.clientHeight
    // 页面总体长度
    const scrollHeight = document.documentElement.scrollHeight

    // 滚动到底部:提前一点刷新
    if (scrollHeight <= scrollTop + clientHeight + 1) {
        homeStore.fetchHouseList()
        console.log('滚动到底部')
    }
}

window.addEventListener('scroll', scrollBottomListener)

onUnmounted(() => {
    window.removeEventListener('scroll', scrollBottomListener)
})

那么自然会想到我们只在mounted时添加此事件的监听。

onMounted(() => {
    window.addEventListener('scroll', scrollBottomListener)
})

封装到一个hook

写到这里我们会发现,并不是只有home页面需要有监听滚动到底部的功能,别的页面也可能需要。因此,我们可以把这个功能抽取出来。

useScoll.js:

// 关于滚动到底部的代码逻辑
import { onMounted, onUnmounted } from "@vue/runtime-core";

export default function useScroll() {

    const scrollBottomListener = () => {
        // 当前位置到顶部的距离
        const scrollTop = document.documentElement.scrollTop
        // 屏幕的长度
        const clientHeight = document.documentElement.clientHeight
        // 页面总体长度
        const scrollHeight = document.documentElement.scrollHeight

        // 滚动到底部:提前一点刷新
        if (scrollHeight <= scrollTop + clientHeight + 1) {
            homeStore.fetchHouseList()
            console.log('滚动到底部')
        }
    }

    onMounted(() => {
        window.addEventListener('scroll', scrollBottomListener)
    })

    onUnmounted(() => {
        window.removeEventListener('scroll', scrollBottomListener)
    })
}

接下来我们只需要在 需要用到监听滚动到底部 这一功能的地方调用 这一函数即可。

这里,我们在home页面需要调用此函数,当判定滚动到底部后需要进行的操作是:动态加载更多的houselist。

接下来将讲两种实现这个功能的方法(名字随便取的):

  • 回调函数法
  • 返回值法

回调函数法(不推荐)

回调函数法,就是在useScoll中传入一个回调函数,来让滚动到底部时调用callback。这样我们就可以每次传入不同的callback来实现不同的功能。

useScroll.js:滚到底部时调用callback

// 关于滚动到底部的代码逻辑
import { onMounted, onUnmounted } from "@vue/runtime-core";

export default function useScroll(callback) {

    const scrollBottomListener = () => {
        // 当前位置到顶部的距离
        const scrollTop = document.documentElement.scrollTop
        // 屏幕的长度
        const clientHeight = document.documentElement.clientHeight
        // 页面总体长度
        const scrollHeight = document.documentElement.scrollHeight

        // 滚动到底部:提前一点刷新
        if (scrollHeight <= scrollTop + clientHeight + 1) {
            console.log('滚动到底部')
            if (callback) callback()
        }
    }

    onMounted(() => {
        window.addEventListener('scroll', scrollBottomListener)
    })

    onUnmounted(() => {
        window.removeEventListener('scroll', scrollBottomListener)
    })
}

home-content:

useScroll(() => {
    homeStore.fetchHouseList()
})

效果:可以实现。

但是这个方法有弊端

  • 回调函数不好管理
  • 不同功能可能需要传入不同数量的回调函数

总而言之,可以实现,但不推荐。

返回值法(推荐)

既然传入一个回调函数并不方便,那么我们可不可以让hook传出一个值,我们根据这个值在对应页面中实现各自的功能?答案是可以的。

useScroll.js:

// 关于滚动到底部的代码逻辑
import { onMounted, onUnmounted } from "@vue/runtime-core";
import { ref } from 'vue'

export default function useScroll() {
    // 初始默认为没有到底
    const isReachBottom = ref(false)

    const scrollBottomListener = () => {
        // 当前位置到顶部的距离
        const scrollTop = document.documentElement.scrollTop
        // 屏幕的长度
        const clientHeight = document.documentElement.clientHeight
        // 页面总体长度
        const scrollHeight = document.documentElement.scrollHeight

        // 滚动到底部:提前一点刷新
        if (scrollHeight <= scrollTop + clientHeight + 1) {
            console.log('滚动到底部')
            isReachBottom.value = true
        }
    }

    onMounted(() => {
        window.addEventListener('scroll', scrollBottomListener)
    })

    onUnmounted(() => {
        window.removeEventListener('scroll', scrollBottomListener)
    })

    return { isReachBottom }
}

home-content:

这里我们用watch来监听数据的变化:Vue.js中 watch(深度监听)的最易懂的解释 - 星期九 - 博客园 (cnblogs.com)

const { isReachBottom } = useScroll()
watch(isReachBottom, (newValue) => {
    if (newValue) {
        homeStore.fetchHouseList()
        isReachBottom.value = false
    }
})

对于homeStore.fetchHouseList()更好的写法是:在它返回promise之后再修改isReachBottom的值:这样意味着它先加载了新数据,再使标记变为“没有到底”。

const { isReachBottom } = useScroll()
watch(isReachBottom, (newValue) => {
    if (newValue) {
        homeStore.fetchHouseList().then(() => {
            isReachBottom.value = false
        })
    }
})

效果

达成目标。

在这里插入图片描述

总代码

修改或添加的文件

在这里插入图片描述

hooks的useScroll

监听窗口滚动到底部的逻辑抽取到hooks。

// 关于滚动到底部的代码逻辑
import { onMounted, onUnmounted } from "@vue/runtime-core";
import { ref } from 'vue'

export default function useScroll() {
    // 初始默认为没有到底
    const isReachBottom = ref(false)

    const scrollBottomListener = () => {
        // 当前位置到顶部的距离
        const scrollTop = document.documentElement.scrollTop
        // 屏幕的长度
        const clientHeight = document.documentElement.clientHeight
        // 页面总体长度
        const scrollHeight = document.documentElement.scrollHeight

        // 滚动到底部:提前一点刷新
        if (scrollHeight <= scrollTop + clientHeight + 1) {
            console.log('滚动到底部')
            isReachBottom.value = true
        }
    }

    onMounted(() => {
        window.addEventListener('scroll', scrollBottomListener)
    })

    onUnmounted(() => {
        window.removeEventListener('scroll', scrollBottomListener)
    })

    return { isReachBottom }
}

home-content

调用hooks。

<template>
    <div class="content">
        <h2>热门精选</h2>

        <div class="list">
            <template v-for="(item, index) in houseList" :key="item.data.houseId">
                <houseItemV9 v-if="item.discoveryContentType === 9" :item="item.data"></houseItemV9>
                <houseItemV3 v-else-if="item.discoveryContentType === 3" :item="item.data"></houseItemV3>
            </template>
        </div>
    </div>
</template>

<script setup>
import { storeToRefs } from "pinia";
import useHomeStore from "../../../store/modules/home";
import houseItemV9 from "../../../components/house-item/house-item-v9.vue";
import houseItemV3 from "../../../components/house-item/house-item-v3.vue";
import useScroll from '@/hooks/useScroll.js'
import { watch } from 'vue'

const homeStore = useHomeStore()
homeStore.fetchHouseList()
const { houseList } = storeToRefs(homeStore)
// console.log(houseList)

const { isReachBottom } = useScroll()
watch(isReachBottom, (newValue) => {
    if (newValue) {
        homeStore.fetchHouseList().then(() => {
            isReachBottom.value = false
        })
    }
})

</script>

<style lang="less" scoped>
.content {
    padding: 0 20px;

    h2 {
        font-size: 20px;
        font-weight: 700;
    }

    .list {
        margin-top: 20px;
        display: flex;
        flex-wrap: wrap;
    }
}
</style>

参考

Element.scrollTop - Web API 接口参考 | MDN (mozilla.org)

Element - Web API 接口参考 | MDN (mozilla.org)

【前端】如何判断是页面滚动还是窗口滚动_karshey的博客-CSDN博客

Vue.js中 watch(深度监听)的最易懂的解释 - 星期九 - 博客园 (cnblogs.com)

生命周期选项 | Vue.js (vuejs.org)

生命周期钩子 | Vue.js (vuejs.org)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/186454.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

git 使用tag

文章目录概述示例创建标签 tag查看tag删除本地标签推送标签git 根据tag创建分支回退到tag参考概述 常常为发布上线某个版本打上一个标签&#xff0c;表示这是什么版本&#xff0c;这样后续找起来就很方便。 如果没有标签只能通过commit历史去查找&#xff0c;而且commit版本显…

每日学术速递1.30

CV - 计算机视觉 | ML - 机器学习 | RL - 强化学习 | NLP 自然语言处理 更多Ai资讯&#xff1a; 今天带来的arXiv上最新发表的3篇文本图像的生成论文。 Subjects: cs.LG、cs.Cv、cs.AI、cs.CL 1.StyleGAN-T: Unlocking the Power of GANs for Fast Large-Scale Text-to-Im…

Spire.Doc for Java v11.1.1 Patch

Spire.Doc for Java是一个专业的 Word API&#xff0c;它使 Java 应用程序能够在不依赖 Microsoft Word 的情况下创建、转换、操作和打印 Word文档。 通过使用这个多功能库&#xff0c;开发人员能够毫不费力地处理大量任务&#xff0c;例如插入图像、超链接、 数字签名、书签和…

Mybatis-plus(下)

一&#xff0c;乐观锁可参考官方文档&#xff1a;https://baomidou.com/pages/0d93c0/场景&#xff1a;当两个工作人员同时去处理一条投诉工单的时候当两个人一起点开了投诉工单详情 并一起编辑处理 随后同时反馈给用户时 此时就会出现矛盾 当系统正常 没有bug的时候 是会出现两…

SpringCloud_Sleuth分布式链路请求跟踪

目录一、概述1.为什么会出现这个技术&#xff1f;需要解决哪些问题&#xff1f;2.是什么3.解决二、搭建链路监控步骤1.zipkin2.服务提供者3.服务消费者&#xff08;调用方&#xff09;4.依次启动eureka7001/8001/805.打开浏览器访问&#xff1a; http://localhost:9411一、概述…

网络流量监控对DMS系统排错分析案例

背景 DMS系统是某汽车集团的经销商在线系统&#xff0c;是汽车集团的重要业务系统。本次分析重点针对DMS系统性能进行分析&#xff0c;以供安全取证、性能分析、网络质量监测以及深层网络分析。 该汽车总部已部署NetInside流量分析系统&#xff0c;使用流量分析系统提供实时和…

Qt扫盲-QDebug理论总结

QDebug理论使用总结一、概述二、使用1. 基础使用2. 格式化选项3.将自定义类型写入流一、概述 每当开发人员需要将调试或跟踪信息写入设备、文件、字符串或控制台时&#xff0c;都会使用QDebug。这个就可以方便我们调试&#xff0c;基本上Qt所有的内容都能通过调试打印出来&…

14.重载运算与类型转换

文章目录重载运算与类型转换14.1基本概念直接调用一个重载的运算符函数某些运算符不应该被重载使用与内置类型一致的含义赋值和复合赋值运算符选择作为成员或者非成员14.2输入和输出运算符14.2.1重载输出运算符<<输出运算符尽量减少格式化操作输入输出运算符必须是非成员…

智能家居之主机---计划筹备

智能家居之主机---计划筹备前言绪言前期构思硬件平台结构平台前言 绪言 感觉有一年多没发过文章了&#xff0c;这一年多太忙了&#xff0c;来到新的公司后要学的太多了&#xff0c;代码风格&#xff0c;架构&#xff0c;操作系统&#xff0c;各种通讯协议&#xff0c;伺服驱动…

解决问题的方法论

概述 解决问题的能力是职场中最重要的能力之一&#xff0c;如何逻辑清晰、效率满满的解决问题&#xff0c;可参考以下4个步骤。 一、准确的界定问题 找出真正的问题。 准确的界定问题&#xff0c;避免被表面现象所迷惑。 《麦肯锡工具》中&#xff0c;给出一个标准的步骤&am…

【数据手册】LM1117L3芯片的使用

1.特征 可调或固定输出1A输出电流低损耗&#xff0c;在1A输出电流时最大电压为1.3V0.04%的线路调节0.2%负载调节100%热极限燃烧快速瞬态响应 2.描述 LM1117系列正可调和固定调节器设计提供1A高电流效率。所有内部电路设计为低至1.3V输入输出差。片内微调将参考电压调整为1% 3…

【微服务】RabbitMQSpringAMQP消息队列

&#x1f6a9;本文已收录至专栏&#xff1a;微服务探索之旅 &#x1f44d;希望您能有所收获 一.初识MQ (1) 引入 微服务间通讯有同步和异步两种方式&#xff1a; 同步通讯&#xff1a;就像打电话&#xff0c;可以立即得到响应&#xff0c;但是你却不能跟多个人同时通话。 异…

leedcode刷题 | 详细注释 | 调用+调试 C++

目录1.两数之和题目C代码2.两数相加题目代码3. 无重复字符的最长子串题目&#xff1a;代码&#xff1a;4. 合并两个有序数组题目&#xff1a;代码&#xff1a;5.寻找两个正序数组的中位数题目&#xff1a;代码&#xff1a;1.两数之和 题目 给定一个整数数组 nums 和一个整数目…

API 网关策略的二三事

作者暴渊&#xff0c;API7.ai 技术工程师&#xff0c;Apache APISIX Committer。 近些年随着云原生和微服务架构的日趋发展&#xff0c;API 网关以流量入口的角色在技术架构中扮演着越来越重要的作用。API 网关主要负责接收所有请求的流量并进行处理转发至上游服务&#xff0c;…

说一说JVM的垃圾回收器

垃圾回收器1.Serial收集器2.parnew收集器3 .parallel Scavenge收集器4.Serial Old5.parallel old收集器6.cms7. G1 收集器串行&#xff1a;指的是垃圾回收器与用户线程交替进行&#xff0c;这意味着在垃圾回收器执行的时候用户线程需要暂停工作 并行&#xff1a;指的是垃圾回收…

网络知识详解之:CA证书制作实战(Nginx数字证书实战)

网络知识详解之&#xff1a;CA证书制作实战 计算机网络相关知识体系详解 网络知识详解之&#xff1a;TCP连接原理详解网络知识详解之&#xff1a;HTTP协议基础网络知识详解之&#xff1a;HTTPS通信原理剖析&#xff08;对称、非对称加密、数字签名、数字证书&#xff09;网络…

Oracle的学习心得和知识总结(九)|Oracle数据库PL/SQL语言条件选择语句之IF和CASE语句技术详解

目录结构 注&#xff1a;提前言明 本文借鉴了以下博主、书籍或网站的内容&#xff0c;其列表如下&#xff1a; 1、参考书籍&#xff1a;《Oracle Database SQL Language Reference》 2、参考书籍&#xff1a;《PostgreSQL中文手册》 3、EDB Postgres Advanced Server User Guid…

[Lua实战]整理Lua中忽略的问题

整理Lua中忽略的问题1.元表metatable和元方法1.1元方法_index可以设置为table1.2.元方法_index可以设置为函数1.3.元方法_index和_newindex实现只读table2.Lua强制GC方法2.1 collectgarbage()3.协程和线程的区别3.1协程coroutine.create()是同步执行,不是并行,只是切了一个上下…

Day874.MySQL索引选择出错问题 -MySQL实战

MySQL索引选择出错问题 Hi&#xff0c;我是阿昌&#xff0c;今天学习记录的是关于MySQL索引选择出错问题的内容。 写 SQL 语句的时候&#xff0c;并没有主动指定使用哪个索引。也就是说&#xff0c;使用哪个索引是由 MySQL 来确定的。 不知道有没有碰到过这种情况&#xff0…

Android开发进阶—invoke反射及其原理解析

反射的概念 反射:Refelection,反射是Java的特征之一,允许运行中的Java程序获取自身信息,并可以操作类或者对象的内部属性通过反射,可以在运行时获得程序或者程序中的每一个类型的成员活成成员的信息程序中的对象一般都是在编译时就确定下来,Java反射机制可以动态地创建对象并且…