vue无限滚动指令
ginp 人气:8概述
日常的开发当中,为了处理大量数据的情况,一般前端会采用分页展示,可以通过分页插件进行数据的按需分页请求展示,另一种解决大量数据的渲染的方式就是无限滚动,在移动端比较常见,也就是我们常见的滚动到底部加载更多数据,一般web端用下拉加载更多场景不是很多,但是也还是有,比如京东和淘宝的web官方,就用到了无限滚动,通过滚动到底部,然后加载更多数据。总之,无限滚动和分页插件都是为了解决大数据展示的问题,现在介绍下vue中通过自定义指令实现无限滚动。
最终效果
实现原理
在开始敲代码之前,先讲一下无限滚动的原理,首选我们需要之前的,怎么才算滚动到底部,然后我们才能去执行加载更多的函数。
关于高度计算的几个方法
clientHeigt
- 这两个属性用于获取元素块可视区的宽高,该属性包括内边距 padding,但不包括边框 border、外边距 margin 和垂直滚动条
scrollHeight
- 一个元素内容高度的度量,包括由于溢出导致的视图中不可见内容,也就是一个元素宽的实际高度,包含被滚动条卷走的部分。具体看下图:
scrollTop
- 滚动条卷走的高度。,参考下图:
综上
得出滚动条到达底部的计算公式为:clientHeight + scrollTop == scrollHeight
,知道这个之后,我们写逻辑就容易多了,只需要在滚动条到达底部的时候,重新取获取数据就可以了。
目录结构
App.vue
无限滚动首选需要一个固定高度的盒子然后设置 style="overflow: auto" 然后可以根据需要加上滚动结束的限制,比如loading等
<template> <div id="app"> //外层包裹盒子 <div class="infinite-list" v-infinite-scroll.loading.complated.immediate="load" style="overflow: auto" ref="infiniteList" > <ul> <li v-for="i in count" class="infinite-list-item">{{ i }}</li> </ul> //加载中 <p v-if="loading && !complated" class="text">加载中...</p> //结束了 <p v-if="complated" class="text">没有更多了</p> </div> </div> </div> </template> <script> import Velocity from "velocity-animate"; import { DatePicker } from "./components/DatePicker/index"; export default { name: "App", components: { DatePicker, }, data() { count: 1, loading: false, complated: false, }; }, methods: { //滚动到底部的处理逻辑 load() { // 以下是定时器模拟异步数据请求,可根据自己的需求进行变更 this.loading = true; setTimeout(() => { if (this.count >= 15) { this.complated = true; return; } this.count += 3; this.loading = false; }, 1000); }, }, }; </script> <style lang="less"> #app { .infinite-list { height: 300px; width: 500px; border: 1px solid red; li { height: 50px; background: #e8f3fe; margin: 10px; color: #7dbcfc; text-align: center; line-height: 50px; } .text { color: green; text-align: center; line-height: 50px; } } } </style>
./components/v-infinite-scroll/index.js
import { checkArriveBottom } from "./utils"; export default { install(Vue) { Vue.directive("infinite-scroll", { // 指令在插入的时候会执行一次 inserted: function (el, binding, vnode) { const fn = binding.value; const context = vnode.context; let timer = null; // 指令的值必须是一个函数,我们好执行回调 if (typeof fn != "function") { throw new Error("指令value必须为函数"); } // 事件处理函数 function handleScroll() { // 判断滚动条到达底部了,才开始执行回调 if (checkArriveBottom(el)) { // 执行回调的时候,要把this指向组件实例 fn.bind(context)(); } } // 将滚动处理函数挂载到对应组件实例上面,便于组件更新的时候,对设置了loading和complate属性进行移除事件绑定 context.handleScroll = handleScroll; // 如果设置有immediate说明立即执行,则立即执行回调,直到将内容撑满内容区 if (binding?.modifiers?.immediate) { timer = setInterval(() => { // 子元素的总高度大于设置指令的父级包裹元素就表示填满了可视区,停止加载 const childScrollHeight = el.firstElementChild.scrollHeight; if (childScrollHeight >= el.clientHeight) { return clearInterval(timer); } handleScroll(); }, 1500); } // 绑定滚动处理函数 el.addEventListener("scroll", context.handleScroll); }, // 组件更新的时候,会不断触发(最明显就是data中的响应式数据变化,会继续执行update方法) update(el, binding, vnode) { const context = vnode.context; // 如果加载中或者已经加载完了,就移除滚动事件 if ( (binding?.modifiers?.complated && context.complated) || (binding?.modifiers?.loading && context.loading) ) { el.removeEventListener("scroll", context.handleScroll); } else { // 当loading和complate都是false的时候,表示可以继续加载 el.addEventListener("scroll", context.handleScroll); } }, }); }, };
./components/v-infinite-scroll/utils.js
/** * @Description 用于判断滚动条是否到达底部 * @param { Element } * @return { Boolean } **/ export function checkArriveBottom(el) { const clientHeight = el.clientHeight; const scrollTop = el.scrollTop; const scrollHeight = el.scrollHeight; //可以设置>=就行,这里也可以设置距离底部一定距离,自定义,不一定非要到达底部 return clientHeight + scrollTop >= scrollHeight; }
./components/v-infinite-scroll/main.js
import Vue from "vue"; import App from "./App.vue"; import vInfiniteScroll from "./components/v-infinite-scroll"; Vue.use(vInfiniteScroll); Vue.use(myUi); new Vue({ render: (h) => h(App), }).$mount("#app");
总结
完成上述指令,需要先阅读官网自定义指令文档,搞懂具体指令的一些钩子函数的用途以及触发时机,还有就是参数的意义,链接放这里cn.vuejs.org/v2/guide/cu…
加载全部内容