说明
在开发无限加载的功能时, 发现当前列表中的元素有增删会使分页的结果有偏移, 导致后续追加的元素中, 可能会存在当且列表末尾重复的元素, 此时分页查询的参数不适合固定的分页及大小了: 比如此时页码为2, 列表元素20个, 每页加载10个, 此时向列表中添加一个元素, 那么在获取第3页的时候, 上一页末尾的一个元素将与第3页的第一个元素重复
解决方法
想到一种大致可行的方法: 需要实时计算页码和每页的大小; 为了在下一页的元素下标正好从列表末尾后开始, 一个很简单的要素就可以, 就是当前列表长度的最大约数(除自身), 然后就可以计算下一页所需的页码和页大小了
完整代码
使用的Vue的组合式API;
其中size是为了限制约数在页大小范围内
usePage.ts
const findMaxDivisor = (val: number, limit: number): number => {
// 异常参数的情况
if (val < 1 || limit < 1) return 1
const integerVal = Math.floor(val)
const integerLimit = Math.floor(limit)
const candidate = Math.ceil(integerVal / integerLimit) * integerLimit - integerVal;
// 此约数能保证下一页大小与原定大小一致的情况
if (integerVal % candidate == 0) return candidate
for (let i = Math.min(Math.floor(integerVal / 2), integerLimit); i >= 2; i--) {
// 找到符合的约数的情况
if (integerVal % i == 0) return i;
}
// 素数的情况
return 1;
}
export const usePage = (size: number) => {
const infinite = (currentLength: number): PageQuery => {
// 首页
if (!currentLength) {
return {
page: 1,
size: size
}
}
const nextSize = findMaxDivisor(currentLength, size);
return {
page: (currentLength / nextSize) + 1,
size: nextSize
}
}
return {
infinite
}
}
使用
传递分页参数时, 调用infinite()方法, 传递当前列表长度即可
// 每页大小10
const {infinite} = usePage(10)
const fetchPage = ({done}: any): void => {
post<Page<Release>>(Path.Release.page, infinite(releases.value.length))
.then(r => {
if (!r.data.records.length) {
done('empty')
} else {
releases.value.push(...r.data.records)
done('ok')
}
})
.catch(() => done('error'))
}
效果
一共12个元素:
第一次
{"page":1,"size":10}
第二次
{"page":3,"size":5}
第三次
{"page":3,"size":6}
貌似还有优化的地方, 比如第二次其实size按照10就行, for循环中Math.min(Math.floor(integerVal / 2), integerLimit)
取了最小值, 因此变成了5