Vue3+TS+Vant 上拉加载下拉刷新框架

大家好,我是鱼樱!!!

关注公众号【鱼樱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()],
    }),
  ],
});

五、框架特点

  1. 简单调用 : 通过 v-model:loadingv-model:finished 轻松管理加载状态
  2. 功能完整 : 同时支持下拉刷新上拉加载
  3. 类型安全: 使用 TypeScript 提供完整类型定义
  4. 灵活配置: 可定制文本、禁用特定功能等
  5. 暴露方法 : 提供 refresh()reset() 等方法供外部调用

这个框架已经可以满足大多数上拉加载和下拉刷新的场景,并且使用方式非常简单。

相关推荐
悬炫几秒前
闭包、作用域与作用域链:概念与应用
前端·javascript
jiaHang2 分钟前
小程序中通过IntersectionObserver实现曝光统计
前端·微信小程序
打野赵怀真24 分钟前
前端资源发布路径怎么实现非覆盖式发布(平滑升级)?
前端·javascript
顾林海33 分钟前
Flutter Dart 流程控制语句详解
android·前端·flutter
tech_zjf34 分钟前
装饰器:给你的代码穿上品如的衣服
前端·typescript·代码规范
xiejianxin52035 分钟前
如何封装axios和取消重复请求
前端·javascript
parade岁月36 分钟前
从学习ts的三斜线指令到项目中声明类型的最佳实践
前端·javascript
狼性书生38 分钟前
electron + vue3 + vite 渲染进程与渲染进程之间的消息端口通信
前端·javascript·electron
阿里巴巴P8资深技术专家38 分钟前
使用vue3.0+electron搭建桌面应用并打包exe
前端·javascript·vue.js
coder_leon42 分钟前
Vite打包优化实践:从分包到性能提升
前端