Vue3 封装了下拉刷新列表的操作
站长
· 阅读数 16
开头先BB两句
Vue2 中可以使用 mixin 混入对象。公共数据,方法,可以混入。
页面逻辑差不多的时候,引入mixin 可以少写很多代码,提高复用性。一些常用的请求接口的方法,和文件流下载的方法,点击事件,都可以放在mixin文件中。
但是 Vue3 里面,没有了 mixin 混入。如果,要实现类似的功能,应该如何封装呢?
举个栗子,移动端H5页面,最常见的滚动加载,下拉刷新。
移动端下拉刷新列表的常见操作
Vant UI组件库,来实现下拉刷新
List 组件可以与 PullRefresh 组件结合使用,实现下拉刷新的效果。
官网例子
<van-pull-refresh v-model="refreshing" @refresh="onRefresh">
<van-list
v-model:loading="loading"
:finished="finished"
finished-text="没有更多了"
@load="onLoad"
>
<van-cell v-for="item in list" :key="item" :title="item" />
</van-list>
</van-pull-refresh>
自己写的 demo点击下方的详情展开
<template>
<div class="page-box">
<div class="list-box">
<van-pull-refresh v-model="refreshing" @refresh="onRefresh" class="list-refresh">
<van-list
v-model:loading="loading"
:finished="finished"
loading-text="加载中..."
finished-text=""
@load="onLoad"
@offset="10"
>
<div v-for="(item, index) in list" :key="index" class="list-item">
{{ item.name }}
</div>
</van-list>
</van-pull-refresh>
</div>
</div>
</template>
<script>
import { reactive, toRefs, onBeforeMount, onMounted } from "vue";
// 引入防抖函数,如果用户多次频繁操作以最后一次为准
import { debounce } from "@/common/utils.js";
export default {
name: "",
setup() {
console.log("1-开始创建组件-setup");
const state = reactive({
// 加载中
loading: false,
// 加载完成
finished: false,
// 是否刷新
refreshing: false,
// 列表数据
list: [],
//请求第几页
pageNumber: 1,
//每页请求的数量
pageSize: 20,
// 总共多少页
totalPage: 0,
});
onBeforeMount(() => {
console.log("2.组件挂载页面之前执行----onBeforeMount");
});
onMounted(() => {
console.log("3.-组件挂载到页面之后执行-------onMounted");
});
const loadData = async () => {
console.log("加载数据");
let params = {
// 搜索条件
pageNo: state.pageNumber,
pageSize: state.pageSize,
};
const result = {
total: 10,
pages: 1,
records: [
{
id: 1,
name: "张三",
number: "123456789",
},
{
id: 2,
name: "李四",
number: "123456789",
},
{
id: 3,
name: "王五",
number: "123456789",
},
{
id: 4,
name: "赵六",
number: "123456789",
},
{
id: 5,
name: "田七",
number: "123456789",
},
{
id: 6,
name: "孙八",
number: "123456789",
},
{
id: 7,
name: "周九",
number: "123456789",
},
{
id: 8,
name: "吴十",
number: "123456789",
},
{
id: 9,
name: "郑十一",
number: "123456789",
},
{
id: 10,
name: "王十二",
number: "123456789",
},
],
};
const { total, pages, records } = result;
// 总共多少页
state.totalPage = pages;
if (pages) state.totalPage = Math.ceil(total / state.pageSize);
let list = records;
if (state.pageNumber == 1) {
state.list = list;
} else {
state.list.push(...list);
}
state.loading = false;
if (state.pageNumber >= state.totalPage) state.finished = true;
};
// 当组件滚动到底部时,会触发 load 事件,调用 onLoad 方法
const onLoad = () => {
if (!state.refreshing && state.pageNumber < state.totalPage) {
state.pageNumber++;
}
if (state.refreshing) {
state.list = [];
state.refreshing = false;
}
// 加载数据
loadData();
};
// 下拉刷新
const onRefresh = () => {
state.refreshing = true;
state.finished = false;
state.loading = true;
state.pageNumber = 1;
onLoad();
};
const onSearch = () => {
onRefresh();
};
// vue3 里面定义防抖方法,直接在setup里面使用,防抖函数包裹一下
const debounceSearch = debounce(() => {
onSearch();
}, 1000);
return {
...toRefs(state),
debounceSearch,
onLoad,
onRefresh,
onSearch,
};
},
};
</script>
<style scoped lang="scss">
.list-box {
padding-bottom: 0;
}
.list-item {
display: flex;
align-items: center;
justify-content: space-evenly;
height: 28px;
padding: 10px;
border-bottom: 1px solid #f2f2f2;
box-shadow: 0 1px 7px #edeef1;
border-radius: 8px;
margin: 10px 16px;
background: #fff;
color: #969799;
}
</style>
加载数据,下拉刷新,上拉刷新都有了。可惜,不能像Vue2 一样复用。
于是,我参考了唐诗这位大佬的用法
加载数据,和下拉刷新,等一系列操作,封装在 usePage 中
import { reactive, ref } from 'vue'
import { debounce } from "@/common/utils.js";
/**
* @description usePage 接收一个 opts 参数,返回列表所需数据
* @param {Function} opts.getListApi - 获取列表数据的接口
* @param {Function} opts.getListFunc - 执行完 getList 成功后执行的逻辑 有一个 list 参数
*/
export const usePage = (opts) => {
// getListApi 获取列表数据的接口
// getListFunc 执行完 loadData 成功后执行的逻辑 有一个 list 参数
const {
getListApi,
getListFunc = (opts) => { },
} = opts
const page = reactive({
// 加载中
loading: false,
// 加载完成
finished: false,
// 是否刷新
refreshing: false,
// 列表数据
list: [],
//请求第几页
pageNumber: 1,
//每页请求的数量
pageSize: 10,
// 总共多少页
totalPage: 0,
})
// 加载数据
const loadData = async () => {
console.log("加载数据");
getListApi().then((res) => {
if (res == null) return;
const { result } = res;
const { total, pages, records } = result;
// 总共多少页
page.totalPage = pages;
if (pages) page.totalPage = Math.ceil(total / page.pageSize);
let list = records;
if (page.pageNumber == 1) {
page.list = list;
} else {
page.list.push(...list);
}
page.loading = false;
if (page.pageNumber >= page.totalPage) page.finished = true;
getListFunc(list)
})
};
// 当组件滚动到底部时,会触发 load 事件,调用 onLoad 方法
const onLoad = () => {
if (!page.refreshing && page.pageNumber < page.totalPage) {
page.pageNumber++;
}
if (page.refreshing) {
page.list = [];
page.refreshing = false;
}
// 加载数据
loadData();
};
// 下拉刷新
const onRefresh = () => {
page.refreshing = true;
page.finished = false;
page.loading = true;
page.pageNumber = 1;
onLoad();
};
const onSearch = () => {
onRefresh();
};
// vue3 里面定义防抖方法,直接在setup里面使用,防抖函数包裹一下
const debounceSearch = debounce(() => {
onSearch();
}, 1000);
return {
page,
debounceSearch,
onSearch,
onRefresh,
onLoad,
}
}
组件中使用:
<script>
import { reactive, toRefs, onBeforeMount, onMounted } from "vue";
import { usePage } from "@/common/usePage.js";
export default {
name: "",
setup() {
console.log("1-开始创建组件-setup");
const state = reactive({});
onBeforeMount(() => {
console.log("2.组件挂载页面之前执行----onBeforeMount");
});
onMounted(() => {
console.log("3.-组件挂载到页面之后执行-------onMounted");
});
const getListApi = () => {
return new Promise((resolve, reject) => {
console.log("加载数据");
const result = {
total: 10,
pages: 1,
records: [
{
id: 1,
name: "张三",
number: "123456789",
},
{
id: 2,
name: "李四",
number: "123456789",
},
],
};
resolve({ result });
});
};
const getListFunc = (list) => {
console.log(list);
console.log(page.list);
};
// 加载数据,和下拉刷新,等一系列操作,封装在 usePage 中,
const { page, debounceSearch, onSearch, onRefresh, onLoad, loadData } = usePage({
getListApi,
getListFunc,
});
return {
...toRefs(state),
...toRefs(page),
debounceSearch,
onLoad,
onRefresh,
onSearch,
};
},
};
</script>
封装之后,简洁优雅多了。
最后的话
以上,如果对你有用的话,不妨点赞收藏关注一下,谢谢 🙏
😊 微信公众号: OrzR3
💖 不定期更新一些技术类,生活类,读书类的文章。