文件夹目录如下:
使用方式:
<template>
<div
class="time-line"
v-infinite-scroll="{
loadMore: loadMoreItems,
threshold: 100 // 当滚动到距离底部 100 像素时触发加载
}"
>
</div>
</template>
<script lang="ts" setup>
// 下拉懒加载加载数据
const loadMoreItems = () => {
console.log("下拉懒加载");
return new Promise(resolve => {
setTimeout(() => {
timeLineList.value.push(...new Array(10).fill(null).map(() => Math.random() * 99999999));
resolve(true);
}, 1000);
});
};
</script>
infiniteScroll.ts代码如下:
/**
* v-infiniteScroll
* 下拉懒加载
*/
import type { Directive, DirectiveBinding } from "vue";
// 定义一个接口来描述绑定值的类型
interface InfiniteScrollBindingValue {
threshold?: number;
loadMore: () => Promise<void>;
}
// 防抖函数
function debounce<T extends (...args: any[]) => void>(func: T, wait: number) {
let timeout: ReturnType<typeof setTimeout> | undefined;
return function (this: any, ...args: Parameters<T>) {
if (timeout) {
clearTimeout(timeout);
}
timeout = setTimeout(() => func.apply(this, args), wait);
};
}
const infiniteScroll: Directive = {
mounted(el: HTMLElement, binding: DirectiveBinding<InfiniteScrollBindingValue>) {
let isLoading = false;
const threshold = binding.value.threshold || 0;
const handler = debounce(() => {
if (isLoading) return;
const scrollTop = el.scrollTop;
const scrollHeight = el.scrollHeight;
const clientHeight = el.clientHeight;
const scrollBottom = scrollHeight - (scrollTop + clientHeight);
if (scrollBottom <= threshold) {
isLoading = true;
binding.value.loadMore().finally(() => {
isLoading = false;
});
}
}, 200); // 200ms 防抖时间,可以根据需求调整
el.addEventListener("scroll", handler);
// 在 directive 的 `binding` 上保存 `handler` 以便后续访问
(el as any).__infiniteScrollHandler__ = handler;
},
beforeUnmount(el: HTMLElement) {
// 组件销毁时移除事件监听
const handler = (el as any).__infiniteScrollHandler__;
if (handler) {
el.removeEventListener("scroll", handler);
}
}
};
export default infiniteScroll;
index.ts文件如下:
import { App } from "vue";
import infiniteScroll from "./modules/infiniteScroll";
const directivesList: any = {
infiniteScroll
};
const directives = {
install: function (app: App<Element>) {
Object.keys(directivesList).forEach(key => {
// 注册所有自定义指令
app.directive(key, directivesList[key]);
});
}
};
export default directives;
在main.ts中注册:
import { createApp } from "vue";
import App from "./App.vue";
import directives from "@/directives/index";
const app = createApp(App);
app..use(directives).mount("#app");