今天用uniapp开发时想用vue3的组合式函数封装一个分页组件,如果是vue2自然就是用mixin了,因为组合式函数更灵活简洁,而且现在也写习惯了,还是决定封装一个vue3版的。
思路:
因为uniapp特有的对一些小程序生命周期的支持,比如onReachBottom可以直接监听页面到底,因此实现滚动加载其实很简单:
1.到页面底部给页数+1,同时判断是否正在加载中。
2.如果可以继续加载就推入list,然后通过接口返回的total和目前list长度判断后续是否还能继续加载。
3.搭配uni-load-more这个底部的加载样式组件,改变status和显示的文字contentText即可。
很坑的是,uniapp虽然说是支持vue3,文档里几乎没有vue3的示例代码,而且根据论坛里反馈的,对vue3.2的支持还不太好。我本来想尝试用vue3.2的script setup写法,却发现小程序的生命周期,像是分页时会用到的onReachBottom,根本没办法引入。最后只好用setup函数。
代码:
// usePagination.js
import {
reactive,
toRefs
} from 'vue';
export default function usePagination(getData) {
const state = reactive({
status: 'more', // 可选值more loading noMore 和contentText的三个提示语相对应
page: 1,
total: 0,
list: [],
contentText: {
contentdown: '查看更多',
contentrefresh: '加载中',
contentnomore: '没有更多了'
},
isLoadMore: false
});
async function handleReachBottom() {
if (state.status === 'more' && !state.isLoadMore) {
state.isLoadMore = true;
state.page += 1;
await handleLoadMore(getData);
return true;
}
}
async function handleLoadMore(func) {
state.status = 'loading';
console.log('handleLoadMore');
try {
const res = await func();
setList(res)
} catch (error) {
console.error('Error during data loading:', error);
}
}
function setList(res) {
if (state.page === 1) {
state.list = res.list;
} else {
state.list = state.list.concat(res.list);
}
state.total = res.total;
setStatus()
}
function setStatus() {
if (state.list.length === state.total) {
state.contentText.contentdown = '没有更多了';
state.status = 'noMore';
} else {
state.contentText.contentdown = '查看更多';
state.status = 'more';
state.isLoadMore = false;
}
}
return {
...toRefs(state),
setList,
handleReachBottom,
};
}
使用:
<template>
<view class="page">
<view class="content">
<SearchComponent @search="handleSearch" />
<ListComponent :list="list" />
<uni-load-more :status="status" :content-text="contentText" />
</view>
</view>
</template>
<script>
import {
ref,
reactive,
onMounted
} from 'vue';
import SearchComponent from '../../components/SearchComponent';
import ListComponent from './ListComponent';
import usePagination from '../../../utils/usePagination.js';
export default {
components: {
ListComponent,
SearchComponent
},
setup() {
const searchStr = ref('');
const {
page,
list,
status,
contentText,
setList,
handleReachBottom,
} = usePagination(getData);
async function getData() {
console.log('getData success');
try {
// 先模拟请求
const res = await new Promise(resolve => {
setTimeout(() => {
resolve({ list: [''], total: 1 });
}, 500);
});
setList(res);
} catch (error) {
console.error('error:', error);
throw error;
}
}
function handleSearch(val) {
searchStr.value = val;
getData();
}
onMounted(() => {
// 第一次加载数据
getData();
});
return {
searchStr,
handleSearch,
// 分页
page,
list,
status,
contentText,
handleReachBottom,
};
},
onReachBottom() {
this.handleReachBottom();
}
}
</script>
<style scoped lang="scss">
.page {
min-height: 100vh;
background: #fff;
}
</style>
可以看到onReachBottom这个生命周期目前只能用vue2的选项式写法,把handleReachBottom在setup里return暴露出来,然后生命周期,methods等方法里就可以用this访问到。