【uniapp】Vue3移动端滚动加载 分页组件的封装

今天用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访问到。

上一篇:mxnet | 制作rec数据文件-2. 生成 .rec


下一篇:react+redux完成登录页面及token的存取和登录保持