大家好,我是鱼樱!!!
关注公众号【鱼樱AI实验室】
持续每天分享更多前端和AI辅助前端编码新知识~~喜欢的就一起学反正开源至上,无所谓被诋毁被喷被质疑文章没有价值~
一个城市淘汰的自由职业-农村前端程序员(虽然不靠代码挣钱,写文章就是为爱发电),兼职远程上班目前!!!热心坚持分享~~~
下面我会封装一个简单易用的上拉加载下拉刷新组件,基于Vue3、TypeScript和Vant组件库。
目录结构
md
├── components/
│ ├── PullToRefresh/
│ │ ├── index.vue # 主组件
│ │ └── types.ts # 类型定义
一、类型定义文件
首先创建类型定义文件:
typescript
// components/PullToRefresh/types.ts
export interface PullToRefreshProps {
// 是否正在加载更多
loading: boolean;
// 是否已经加载完所有数据
finished: boolean;
// 是否禁用下拉刷新
disablePullRefresh?: boolean;
// 是否禁用上拉加载
disableLoadMore?: boolean;
// 加载中的提示文本
loadingText?: string;
// 加载完成的提示文本
finishedText?: string;
// 加载失败的提示文本
errorText?: string;
// 每页数据条数
pageSize?: number;
}
export interface PullToRefreshEmits {
// 触发刷新事件
(e: 'refresh'): void;
// 触发加载更多事件,传递当前页码
(e: 'load-more', page: number): void;
// 更新加载状态
(e: 'update:loading', loading: boolean): void;
// 更新完成状态
(e: 'update:finished', finished: boolean): void;
}
二、主组件实现
html
<!-- components/PullToRefresh/index.vue -->
<template>
<van-pull-refresh
v-if="!props.disablePullRefresh"
v-model="isRefreshing"
@refresh="onRefresh"
>
<van-list
v-model:loading="isLoading"
:finished="props.finished"
:finished-text="props.finishedText"
:loading-text="props.loadingText"
:error-text="props.errorText"
:immediate-check="false"
@load="onLoadMore"
>
<slot></slot>
</van-list>
</van-pull-refresh>
<van-list
v-else
v-model:loading="isLoading"
:finished="props.finished"
:finished-text="props.finishedText"
:loading-text="props.loadingText"
:error-text="props.errorText"
:immediate-check="false"
@load="onLoadMore"
>
<slot></slot>
</van-list>
</template>
<script lang="ts" setup>
import { ref, computed, watch } from 'vue';
import type { PullToRefreshProps, PullToRefreshEmits } from './types';
const props = withDefaults(defineProps<PullToRefreshProps>(), {
disablePullRefresh: false,
disableLoadMore: false,
loadingText: '加载中...',
finishedText: '没有更多了',
errorText: '请求失败,点击重新加载',
pageSize: 10
});
const emit = defineEmits<PullToRefreshEmits>();
// 当前页码
const currentPage = ref(1);
// 是否正在刷新
const isRefreshing = ref(false);
// 计算loading状态
const isLoading = computed({
get: () => props.loading,
set: (value) => emit('update:loading', value)
});
// 监听loading变化
watch(() => props.loading, (newVal) => {
if (!newVal) {
isRefreshing.value = false;
}
});
// 下拉刷新
const onRefresh = () => {
// 重置页码
currentPage.value = 1;
// 触发刷新事件
emit('refresh');
};
// 上拉加载更多
const onLoadMore = () => {
if (props.disableLoadMore || props.finished) return;
// 触发加载更多事件,传递当前页码
emit('load-more', currentPage.value);
// 页码自增
currentPage.value++;
};
// 暴露方法给父组件
defineExpose({
// 手动触发刷新
refresh: onRefresh,
// 重置组件状态
reset: () => {
currentPage.value = 1;
emit('update:finished', false);
},
// 获取当前页码
getCurrentPage: () => currentPage.value
});
</script>
三、使用示例
下面是一个完整的使用示例:
html
<template>
<div class="container">
<pull-to-refresh
v-model:loading="loading"
v-model:finished="finished"
@refresh="onRefresh"
@load-more="onLoadMore"
ref="pullRefreshRef"
>
<div v-for="item in list" :key="item.id" class="item">
{{ item.name }}
</div>
</pull-to-refresh>
</div>
</template>
<script lang="ts" setup>
import { ref } from 'vue';
import PullToRefresh from '@/components/PullToRefresh/index.vue';
// 数据列表
const list = ref<Array<{ id: number; name: string }>>([]);
// 加载状态
const loading = ref(false);
// 是否已加载完成
const finished = ref(false);
// 组件引用
const pullRefreshRef = ref();
// 模拟请求数据
const fetchData = (page: number, refresh = false) => {
loading.value = true;
// 模拟网络请求
setTimeout(() => {
const newData = Array.from({ length: 10 }, (_, index) => ({
id: page * 10 + index,
name: `项目 ${page * 10 + index}`
}));
if (refresh) {
// 刷新时替换数据
list.value = newData;
} else {
// 加载更多时追加数据
list.value.push(...newData);
}
// 判断是否已经加载完所有数据
if (page >= 3) {
finished.value = true;
}
loading.value = false;
}, 1000);
};
// 下拉刷新
const onRefresh = () => {
finished.value = false;
fetchData(1, true);
};
// 上拉加载更多
const onLoadMore = (page: number) => {
fetchData(page);
};
// 初始加载
fetchData(1, true);
</script>
<style scoped>
.container {
height: 100vh;
overflow: hidden;
}
.item {
padding: 16px;
border-bottom: 1px solid #eee;
}
</style>
四、项目配置
确保你的项目已经安装配置了Vant:
bash
# 安装Vant
npm i vant
推荐使用按需引入方式(使用 unplugin-vue-components):
bash
# 安装按需引入插件
npm i unplugin-vue-components -D
在 vite.config.ts
中配置:
typescript
// vite.config.ts
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
import Components from 'unplugin-vue-components/vite';
import { VantResolver } from 'unplugin-vue-components/resolvers';
export default defineConfig({
plugins: [
vue(),
Components({
resolvers: [VantResolver()],
}),
],
});
五、框架特点
- 简单调用 : 通过
v-model:loading
和v-model:finished
轻松管理加载状态 - 功能完整 : 同时支持下拉刷新 和上拉加载
- 类型安全: 使用 TypeScript 提供完整类型定义
- 灵活配置: 可定制文本、禁用特定功能等
- 暴露方法 : 提供
refresh()
和reset()
等方法供外部调用
这个框架已经可以满足大多数上拉加载和下拉刷新的场景,并且使用方式非常简单。