实现vue无限滚动指令
概述
日常的开发当中,为了处理大量数据的情况,一般前端会采用分页展示,可以通过分页插件进行数据的按需分页请求展示,另一种解决大量数据的渲染的方式就是无限滚动,在移动端比较常见,也就是我们常见的滚动到底部加载更多数据,一般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…
转载自:https://juejin.cn/post/7125696279857872909