微信小程序uni-app+vue3实现局部上下拉刷新和scroll-view动态高度计算
前言
在uni-app+vue3项目开发中,经常需要实现列表的局部上下拉刷新功能。由于网上相关教程较少且比较零散,本文将详细介绍如何使用scroll-view组件实现这一功能,包括动态高度计算、下拉刷新、上拉加载等完整实现。
重要提示
⚠️ triggered状态管理是关键
- triggered必须手动管理其true/false状态
- 不正确的状态管理会导致下拉刷新卡住
- 需要在适当的时机重置状态
核心实现思路
- 状态定义
// 下拉刷新相关状态
const triggered = ref(false) // 触发下拉刷新状态
const refresherEnabled = ref(true) // 是否启用下拉刷新
- 关键事件处理
// 下拉过程中触发
const onPulling = () => {
triggered.value = true // 手动设置为true
}
// 刷新结束时触发
const onRestore = () => {
triggered.value = false // 手动重置为false
}
// 刷新中断时触发
const onAbort = () => {
triggered.value = false // 手动重置为false
}
// 刷新数据处理
const refreshHistoryList = async () => {
triggered.value = true // 开始刷新时设置为true
try {
// 执行刷新逻辑
await loadData()
} catch (err) {
console.error('刷新失败:', err)
} finally {
triggered.value = false // 完成后一定要设置为false
}
}
- 组件配置
<scroll-view
:refresher-enabled="refresherEnabled"
:refresher-triggered="triggered"
@refresherrefresh="refreshHistoryList"
@refresherpulling="onPulling"
@refresherrestore="onRestore"
@refresherabort="onAbort"
>
<!-- 列表内容 -->
</scroll-view>
注意事项
-
状态重置时机
- 刷新完成时必须重置
- 刷新中断时必须重置
- 组件卸载时最好重置
-
常见问题
- 忘记重置导致卡住
- 重置时机不对导致异常
- 多个事件重复设置状态
-
最佳实践
- 使用try/finally确保重置
- 统一状态管理位置
- 添加适当的错误处理
快速开始
1. 基础配置
2. 组件结构
在template中创建scroll-view组件:
<template>
<scroll-view
:style="{ height: scrollViewHeight }"
:scroll-y="true"
:refresher-enabled="refresherEnabled"
@refresherrefresh="refreshHistoryList"
@refresherpulling="onPulling"
@scroll="roll"
:enable-passive="true"
:show-scrollbar="false"
@refresherrestore="onRestore"
@refresherabort="onAbort"
:refresher-triggered="triggered"
ref="scrollView"
>
<!-- 列表内容 -->
<view class="empty-state" v-show="!list.length">
暂无数据
</view>
<view v-for="(item, index) in list" :key="index">
<!-- 列表项内容 -->
</view>
<up-loadmore v-if="list.length>0" :status="scrollStatus" @loadmore="loadmore"/>
</scroll-view>
</template>
注意uwiewPlus组件库up-loadmore是通过@loadmore来触发@click的效果的。
3. 核心状态定义
属性/事件 | 类型 | 默认值 | 必填 | 说明 |
---|---|---|---|---|
style.height | string | - | 是 | 设置scroll-view高度,不设置无法滚动 |
scroll-y | boolean | false | 否 | 是否允许纵向滚动 |
scroll-x | boolean | false | 否 | 是否允许横向滚动 |
refresher-enabled | boolean | false | 否 | 是否开启下拉刷新功能 |
refresher-triggered | boolean | false | 否 | 当前下拉刷新状态 |
enable-passive | boolean | false | 否 | 是否开启被动监听滚动事件 |
show-scrollbar | boolean | true | 否 | 是否显示滚动条 |
@refresherrefresh | function | - | 否 | 下拉刷新触发时的回调 |
@refresherpulling | function | - | 否 | 下拉过程中触发的回调 |
@refresherrestore | function | - | 否 | 下拉刷新复位时触发的回调 |
@refresherabort | function | - | 否 | 下拉刷新被中止时触发的回调 |
@scroll | function | - | 否 | 滚动时触发的回调函数 |
ref | string | - | 否 | 组件的引用标识 |
// 下拉刷新相关
const triggered = ref(false) // 触发下拉刷新状态
const refresherEnabled = ref(true) // 是否启用下拉刷新
const currentScrollTop = ref(0) // 当前滚动位置
const scrollViewHeight = ref('400rpx') // scroll-view高度
// 分页相关
const currentPage = ref(1) // 当前页码
const pageSize = ref(10) // 每页数量
const totalPage = ref(0) // 总页数
const scrollStatus = ref<string>('loadmore') // 滚动状态
4. 动态高度计算
onReady(() => {
// 获取系统信息
const systemInfo = uni.getSystemInfoSync()
const windowHeight = systemInfo.windowHeight
// 获取各元素高度
const query = uni.createSelectorQuery()
Promise.all([
new Promise<number>(resolve => {
query.select('.u-demo-block').boundingClientRect(data => {
resolve(data?.height || 0)
}).exec()
}),
// ... 获取其他元素高度
]).then(([demoBlockHeight, titleHeight, uploadHeight]) => {
// 计算scroll-view高度
const scrollHeight = windowHeight - demoBlockHeight - titleHeight - uploadHeight - 40
scrollViewHeight.value = `${scrollHeight}rpx`
})
})
核心功能实现
1. 下拉刷新
// 下拉刷新处理
const refreshHistoryList = async () => {
triggered.value = true
currentPage.value = 1
try {
const res = await loadFirstPage()
list.value = res.data
updateLoadStatus()
} catch (err) {
console.error('刷新失败:', err)
} finally {
triggered.value = false
}
}
2. 上拉加载
// 上拉加载更多
const loadmore = async () => {
if (scrollStatus.value === 'noMore') return
scrollStatus.value = 'loading'
try {
const res = await loadMoreData(++currentPage.value)
list.value.push(...res.data)
updateLoadStatus()
} catch (err) {
console.error('加载失败:', err)
scrollStatus.value = 'loadmore'
}
}
3. 滚动事件处理
const roll = (e: any) => {
currentScrollTop.value = e.detail.scrollTop
// 控制下拉刷新启用状态
refresherEnabled.value = e.detail.scrollTop < 50
// 触底加载更多
const scrollBottom = e.detail.scrollHeight - e.detail.scrollTop - e.detail.height
if (scrollBottom < 50 && scrollStatus.value === 'loadmore') {
loadmore()
}
}
注意事项
-
高度计算
- 需要考虑所有固定元素的高度
- 使用rpx单位确保跨设备兼容性
- 预留适当边距避免内容被遮挡
-
性能优化
- 使用enable-passive提升滚动性能
- 合理控制刷新频率
- 避免频繁触发加载更多
-
用户体验
- 添加加载提示
- 保持滚动位置
- 合理控制刷新触发时机
加载更多组件使用
⚠️ up-loadmore组件使用说明
- 基础配置
<up-loadmore
v-if="list.length>0"
:status="scrollStatus"
@loadmore="loadmore"
loadmore-text="加载更多"
/>
- 状态管理
// 加载状态
const scrollStatus = ref<string>('loadmore') // 可选值: loadmore/loading/noMore
// 加载更多处理
const loadmore = async () => {
if (scrollStatus.value === 'noMore') return
scrollStatus.value = 'loading' // 设置加载中状态
try {
const res = await loadMoreData(++currentPage.value)
list.value.push(...res.data)
// 更新加载状态
if (currentPage.value >= totalPage.value) {
scrollStatus.value = 'noMore'
} else {
scrollStatus.value = 'loadmore'
}
} catch (err) {
console.error('加载失败:', err)
scrollStatus.value = 'loadmore'
}
}
- 触发方式
- 通过
@loadmore
事件监听触发加载 - 可以在scroll-view滚动到底部时自动触发
- 也可以点击"加载更多"文本手动触发
- 状态说明
loadmore
: 可以加载更多loading
: 正在加载中noMore
: 没有更多数据
完整示例
<scroll-view
@scroll="roll"
:scroll-y="true"
>
<!-- 列表内容 -->
<view v-for="(item, index) in list" :key="index">
{{ item }}
</view>
<!-- 加载更多组件 -->
<up-loadmore
v-if="list.length>0"
:status="scrollStatus"
@loadmore="loadmore"
loadmore-text="加载更多"
/>
</scroll-view>
<script setup lang="ts">
const scrollStatus = ref('loadmore')
const currentPage = ref(1)
const list = ref([])
// 滚动触底自动加载
const roll = (e: any) => {
const scrollBottom = e.detail.scrollHeight - e.detail.scrollTop - e.detail.height
if (scrollBottom < 50 && scrollStatus.value === 'loadmore') {
loadmore()
}
}
// 加载更多处理
const loadmore = async () => {
if (scrollStatus.value === 'noMore') return
scrollStatus.value = 'loading'
try {
const res = await loadMoreData(currentPage.value++)
list.value.push(...res.data)
scrollStatus.value = res.hasMore ? 'loadmore' : 'noMore'
} catch (err) {
console.error('加载失败:', err)
scrollStatus.value = 'loadmore'
}
}
</script>
总结
通过本教程的学习,我们掌握了:
- scroll-view组件的基本使用
- 动态高度计算方法
- 下拉刷新实现
- 上拉加载更多功能
- 滚动位置维护
参考资料
- 微信小程序官方文档
- Vue3文档
admore’ : ‘noMore’
} catch (err) {
console.error(‘加载失败:’, err)
scrollStatus.value = ‘loadmore’
}
}
## 总结
通过本教程的学习,我们掌握了:
1. scroll-view组件的基本使用
2. 动态高度计算方法
3. 下拉刷新实现
4. 上拉加载更多功能
5. 滚动位置维护
## 参考资料
- [微信小程序官方文档](https://developers.weixin.qq.com/miniprogram/dev/component/scroll-view.html)
- [Vue3文档](https://v3.cn.vuejs.org/)
- [uview-plus文档](https://uiadmin.net/uview-plus/)