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() 等方法供外部调用

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

相关推荐
MrsBaek11 分钟前
前端笔记-JavaScript部分(中)
前端·javascript·笔记
艾露z17 分钟前
Vert.x学习(五)—— SockJS,搭建客户端,与后端服务器进行通信
java·前端·后端·学习·web
—Qeyser24 分钟前
用 Deepseek 写的uniapp油耗计算器
前端·vue.js·gpt·chatgpt·uni-app·gpt-3·deepseek
溪饱鱼24 分钟前
DHgate爆火背后的技术原因
android·前端·ios
Mike_jia24 分钟前
Beszel服务器监控系统全栈指南:从部署到企业级实战
前端
拖孩26 分钟前
【Nova UI】九、打造组件库第一个组件-图标组件(中):属性、功能与样式的完美融合
前端·javascript·vue.js
风兮w37 分钟前
插件架构实践
前端·javascript·架构
天天扭码1 小时前
一分钟解决 | 高频面试算法题——最长连续序列(哈希表)
前端·javascript·算法
WEI_Gaot1 小时前
3 使用工厂模式 和 构造函数 优化创建对象
前端·javascript
程序员小续1 小时前
useContext 用法全解析:3 个实战案例带你上手!
前端·react.js·面试