開(kāi)發(fā)移動(dòng)端頁(yè)面遇到大量的下拉刷新上拉加載模塊祟同?
每次都需要維護(hù)當(dāng)前頁(yè)數(shù)還有數(shù)據(jù)更新后的總頁(yè)數(shù)處理?
如果這些操作都可以用一行代碼實(shí)現(xiàn):
// 下拉刷新
pageViewModel.headerRefreshing();
// 上拉加載
pageViewModel.footerRefreshing();
感興趣的同學(xué)可以先戳這個(gè)鏈接先行體驗(yàn)一下效果,也可以戳這個(gè)鏈接看一下源代碼
接下來(lái)我們好好聊一聊怎么實(shí)現(xiàn)一行代碼完成分頁(yè)模塊
功能分解
1、首先我們需要思考的是分頁(yè)相關(guān)參數(shù)的處理
也就是 pageSize(單頁(yè)數(shù)目)
、 currentPage(當(dāng)前頁(yè))
和 totalPage(總頁(yè)數(shù))
我們需要一個(gè)實(shí)例來(lái)維護(hù)和更新這些參數(shù)
這里我設(shè)計(jì)了一個(gè) class
class PagingViewModel<T> {
private currentPage: Ref<number>;
private totalPage: Ref<number>;
private pageSize: number;
// 狀態(tài)變量
refreshing: Ref<boolean>;
loading: Ref<boolean>;
finished: Ref<boolean>;
constructor(opt: PagingOptions<T>) {
this.pageSize = opt.pageSize ?? 20;
this.currentPage = ref(1);
this.totalPage = ref(1);
this.refreshing = ref(false);
this.loading = ref(false);
this.finished = ref(false);
}
}
使用 PagingViewModel
來(lái)管理這些參數(shù)轰驳,在觸發(fā)刷新和加載行為之后實(shí)時(shí)更新,并且通過(guò) 3 個(gè)狀態(tài)變量(刷新中弟灼、加載中级解、已全部加載)來(lái)標(biāo)識(shí)此時(shí)的 Paging
加載狀態(tài)
2、其次我們需要考慮數(shù)據(jù)的來(lái)源與更新
這里主要是體現(xiàn)在通過(guò)接口獲取數(shù)據(jù)這一塊
也就是 params(請(qǐng)求參數(shù))
田绑、requestFn(請(qǐng)求體)
勤哗、datas(用于渲染的數(shù)據(jù)源)
這里我們?cè)賹⑦@些內(nèi)容合并進(jìn)上面的 class 中
class PagingViewModel<T> {
private currentPage: Ref<number>;
private totalPage: Ref<number>;
private pageSize: number;
// 新增屬性
private params: any;
private datas: Ref<Array<T>>;
private requestFn: (params: any) => Promise<any>;
private responseCompletion?: (vm: PagingViewModel<T>, datas: Array<any>) => Array<any>;
refreshing: Ref<boolean>;
loading: Ref<boolean>;
finished: Ref<boolean>;
constructor(opt: PagingOptions<T>) {
this.requestFn = opt.requestFn;
this.params = opt.params || {};
this.datas = opt.datas;
this.pageSize = opt.pageSize ?? 20;
this.currentPage = ref(1);
this.totalPage = ref(1);
this.refreshing = ref(false);
this.loading = ref(false);
this.finished = ref(false);
}
}
現(xiàn)在我們的 class
已經(jīng)初具雛形,可以為其增加行為 loadData()
/**
* 加載數(shù)據(jù)
*/
loadData() {
// 組合基本的請(qǐng)求參數(shù)
const params = {
currentPage: this.currentPage.value,
pageSize: this.pageSize,
...this.params,
};
// 刷新時(shí)清空數(shù)據(jù)源
if (this.currentPage.value === 1) {
this.datas.value = [] as Array<T>;
}
// 網(wǎng)絡(luò)請(qǐng)求主體
this.requestFn(params).then((response) => {
if (!response) {
return;
}
// 記錄總頁(yè)數(shù)
this.totalPage.value = response.totalPage;
let resDatas = response.responseObject.items;
if (this.responseCompletion) {
// 需要特殊處理的數(shù)據(jù)源由該 hook 完成
resDatas = this.responseCompletion(this, response.datas);
}
// 處理第一頁(yè)和其他頁(yè)數(shù)據(jù)組合
if (this.currentPage.value === 1) {
this.datas.value = resDatas;
} else {
this.datas.value = this.datas.value.concat(resDatas);
}
// 加載結(jié)束掩驱,更新?tīng)顟B(tài)
this.didLoaded();
});
}
/**
* 加載結(jié)束芒划,狀態(tài)更新
*/
didLoaded() {
// 處理第一頁(yè)和其他頁(yè)的狀態(tài)
if (this.currentPage.value === 1) {
this.refreshing.value = false;
this.finished.value = false;
} else {
this.loading.value = false;
}
}
這里可能有小伙伴就有疑問(wèn)了,如果我需要對(duì)網(wǎng)絡(luò)請(qǐng)求拿到的數(shù)據(jù)源進(jìn)行處理后才提供給界面渲染怎么辦欧穴?
那我們就增加一個(gè)Hook (responseCompletion)
來(lái)處理接口數(shù)據(jù)
剩下就是處理 currentPage
為 1 的情況和不為 1 的情況了
3民逼、最后實(shí)現(xiàn)上拉加載和下拉刷新
/**
* 下拉刷新
*/
headerRefreshing() {
// 重置當(dāng)前頁(yè)并刷新數(shù)據(jù)
this.currentPage.value = 1;
this.loadData();
}
/**
* 上拉加載
*/
footerRefreshing() {
// 這里根據(jù) totalPage 控制顯示沒(méi)有更多數(shù)據(jù)
if (this.currentPage.value === this.totalPage.value) {
this.finished.value = true;
this.didLoaded();
return;
}
// 當(dāng)前頁(yè)自增并請(qǐng)求下一頁(yè)數(shù)據(jù)
this.currentPage.value += 1;
this.loadData();
}
到此,我們的 PagingViewModel 已經(jīng)可以正常工作啦~
這里提供一下最基礎(chǔ)的調(diào)用方式
// 網(wǎng)絡(luò)請(qǐng)求
const requestFn = (params: any) => {
return new Promise<any>((resolve) => {
// 這里模擬請(qǐng)求數(shù)據(jù)
const response = mockDatas(params.currentPage, params.pageSize);
resolve(response);
});
}
// 渲染數(shù)據(jù)源
const datas = ref<ResponseObject[]>([]);
// 分頁(yè)vm
const pageViewModel = new PagingViewModel<any>({
requestFn,
datas,
pageSize: 5,
});
// 刷新數(shù)據(jù)
pageViewModel.headerRefreshing();
// 加載數(shù)據(jù)
pageViewModel.footerRefreshing();
總結(jié)
這里想和大家分享的只是構(gòu)建快捷分頁(yè)模型的一個(gè)思路涮帘,具體細(xì)節(jié)的雕琢還需要深入思考
很多分頁(yè)參數(shù)的命名定義可能會(huì)有所不同拼苍,返回值的數(shù)據(jù)結(jié)構(gòu)也會(huì)有所不同
所以,大家領(lǐng)悟思路就好调缨,可以根據(jù)這個(gè)思路做一套適合自己業(yè)務(wù)的分頁(yè)模型