UniApp + Vue3 实战:打造高性能下拉刷新 & 上拉加载列表
一、需求分析
二、技术准备
三、代码实现
1. 页面结构(template)
<template><viewclass="list-page"><!-- 列表容器 --><scroll-viewclass="list-container"scroll-yrefresher-enabled:refresher-triggered="refreshing"@refresherrefresh="handleRefresh"@scrolltolower="handleLoadMore"><!-- 列表内容 --><viewclass="list-item"v-for="(item, index) in listData":key="index"><viewclass="item-title">{{ item.title }}</view><viewclass="item-desc">{{ item.desc }}</view></view><!-- 状态提示 --><uni-load-more:status="loadStatus":content-text="loadText"class="load-more"/></scroll-view><!-- 初始加载/加载失败占位 --><viewclass="empty-state"v-if="!listData.length && !loading"><textclass="empty-text">{{ emptyText }}</text><buttonclass="reload-btn" @click="fetchList(1)"v-if="loadFailed">重新加载</button></view><!-- 初始加载中 --><viewclass="loading-state"v-if="loading && !listData.length"><uni-load-morestatus="loading" /></view></view></template>
2. 逻辑实现(script setup)
<scriptsetup>import { ref, reactive, onMounted } from 'vue'// 列表核心状态const listData = ref([]) // 列表数据const loading = ref(false) // 加载中(初始/加载更多)const refreshing = ref(false) // 下拉刷新中const loadFailed = ref(false) // 加载失败const loadStatus = ref('more') // 加载更多状态:more-可加载,noMore-无更多,error-失败const pageInfo = reactive({ // 分页信息page: 1,size: 10,total: 0})// 加载文案自定义const loadText = ref({contentdown: '上拉加载更多',contentrefresh: '正在加载...',contentnomore: '已加载全部数据'})const emptyText = ref('加载中...')/*** 模拟接口请求(实际项目替换为真实接口)* @param {Number} page 页码* @returns {Promise} 数据Promise*/const fetchApi = (page) => {return new Promise((resolve, reject) => {// 模拟网络延迟setTimeout(() => {// 模拟随机失败(便于测试失败场景)if (Math.random() < 0.1 && page !== 1) {reject(new Error('接口请求失败'))return}// 模拟数据const mockData = Array.from({ length: pageInfo.size }, (_, i) => ({id: (page - 1) * pageInfo.size + i + 1,title: `列表项 ${(page - 1) * pageInfo.size + i + 1}`,desc: `这是第 ${page} 页的第 ${i + 1} 条数据`}))// 模拟总条数(共3页数据)const total = 28resolve({ list: mockData, total })}, 800)})}/*** 获取列表数据* @param {Number} page 页码(1表示刷新,其他表示加载更多)*/const fetchList = async (page) => {// 避免重复请求if (loading.value) returnloading.value = trueloadFailed.value = falseemptyText.value = '加载中...'try {const res = await fetchApi(page)// 刷新:重置列表;加载更多:追加列表if (page === 1) {listData.value = res.list} else {listData.value = [...listData.value, ...res.list]}// 更新分页信息pageInfo.page = pagepageInfo.total = res.total// 判断是否有更多数据if (listData.value.length >= pageInfo.total) {loadStatus.value = 'noMore'} else {loadStatus.value = 'more'}// 空数据提示if (!listData.value.length) {emptyText.value = '暂无数据'}} catch (err) {console.error('列表加载失败:', err)loadFailed.value = trueloadStatus.value = 'error'emptyText.value = '加载失败,请重试'} finally {loading.value = falserefreshing.value = false // 结束下拉刷新状态// 停止UniApp原生下拉刷新(兼容非scroll-view的下拉刷新)uni.stopPullDownRefresh()}}/*** 下拉刷新处理*/const handleRefresh = () => {if (refreshing.value) returnrefreshing.value = truepageInfo.page = 1 // 重置页码fetchList(1)}/*** 上拉加载更多处理*/const handleLoadMore = () => {// 过滤无效触发:加载中、无更多、加载失败if (loading.value || loadStatus.value === 'noMore' || loadStatus.value === 'error') {return}fetchList(pageInfo.page + 1)}// 页面初始化加载onMounted(() => {fetchList(1)})</script>
3. 样式美化(style)
<stylescoped>.list-page {width: 100%;height: 100vh;background-color: #f5f5f5;}.list-container {width: 100%;height: 100%;}.list-item {padding: 20rpx;margin: 10rpx;background-color: #fff;border-radius: 10rpx;}.item-title {font-size: 32rpx;font-weight: 600;margin-bottom: 10rpx;}.item-desc {font-size: 28rpx;color: #666;}.load-more {padding: 20rpx 0;text-align: center;}.empty-state, .loading-state {display: flex;flex-direction: column;align-items: center;justify-content: center;height: 50vh;}.empty-text {font-size: 28rpx;color: #999;margin-bottom: 20rpx;}.reload-btn {padding: 10rpx 30rpx;background-color: #007aff;color: #fff;border-radius: 20rpx;font-size: 28rpx;}</style>
四、关键点解析
1. 状态管理
使用ref管理简单类型状态(如loading、refreshing),reactive管理复杂对象(如pageInfo),符合 Vue3 组合式 API 最佳实践。
拆分不同状态(加载中、刷新中、加载失败、无更多),避免状态混乱,提升代码可维护性。
2. 防重复请求
在fetchList开头判断loading状态,避免同一时间多次触发请求。
上拉加载更多时,过滤加载中、无更多、加载失败等无效状态,防止无效请求。
3. 兼容与体验优化
使用scroll-view的下拉刷新(refresher-enabled)而非 UniApp 全局下拉刷新,更灵活可控。
最终通过uni.stopPullDownRefresh()兼容全局下拉刷新,确保状态统一。
区分 “初始加载” 和 “加载更多” 的状态提示,提升用户体验。
4. 接口适配
封装fetchApi函数,实际项目中只需替换为真实接口请求(如使用uni.request或封装的 axios),核心逻辑无需改动。
五、功能扩展建议
骨架屏:替换简单的加载提示,使用骨架屏提升视觉体验。
夜雨聆风
