业务场景如下
最近自己的小程序接入了开源的菜谱接口(上个原型图),数据量上w,目前只是做了最简单的节流

代码如下 欢迎大佬们CR
js
const loading = ref(false)
const finished = ref(false)
const getMoreFood = async () => {
// 如果正在加载或已经加载完毕,直接返回
if (loading.value || finished.value) return
loading.value = true
try {
const resC = await getCookListById(categoryId.value, currentPage.value)
// 兼容后端返回格式
const list = Array.isArray(resC?.data?.data?.list) ? resC.data.data.list : []
// 如果本次返回没有数据,说明到底了
if (list.length === 0) {
finished.value = true
uni.showToast({
title: '没有更多啦~',
icon: 'none'
})
} else {
cookList.value = cookList.value.concat(list)
currentPage.value++ // 只有有数据时才自增页码
}
console.log('加载更多', cookList.value)
} catch (e) {
// 错误处理
console.error('加载失败', e)
} finally {
loading.value = false
}
}
但是问题也显而易见,用户如果不断下拉cookList会越来越大,所以带着问题去问了GPT,给出的一个方案如下
js
const BUFFER = 2; // 缓存前后各 2 页
let currentPage = 1;
const cache = new Map(); // pageNo -> items[]
async function loadPage(page) {
if (!cache.has(page)) {
cache.set(page, (await fetchPage(page)).data);
}
currentPage = page;
// 清理过期页
for (let key of cache.keys()) {
if (Math.abs(key - currentPage) > BUFFER) {
cache.delete(key);
}
}
return cache.get(page);
}
很好理解,BUFFER参数控制了缓存的页数也就对应列表的数量
那么我们来模拟下:
用户一开始请求page-1,loadPage函数缓存没有命中,去请求,然后返回数据
然后是page-2,page-3,请求page-4时候Math.abs(1 - 4) > BUFFER 会把page-1的数据清理掉
当用户往回滚动,page-2,page-3都命中缓存,page-1被清理了触发!cache.has(page)会请求一次且page-4会被清理掉
拓展
上述的内容讲的是基于滑动窗口的优化思路,下面给出 Pagination 分页的优化思路
LRU全称Least Recently Used,即最近最少使用页面置换算法,学过操作系统的肯定不陌生
js
class LRUCache {
constructor(maxPages, fetchPageFn) {
this.maxPages = maxPages;
this.cache = new Map(); // 按插入顺序维护
this.fetchPage = fetchPageFn; // 外部传入的分页请求函数: async (pageNo) => data[]
}
async get(pageNo) {
// 1. 如果命中:刷新顺序,直接返回
if (this.cache.has(pageNo)) {
const data = this.cache.get(pageNo);
// 拿出来再放回去,变成最新
this.cache.delete(pageNo);
this.cache.set(pageNo, data);
return data;
}
// 2. 缓存未命中:去后端拉取
const data = await this.fetchPage(pageNo);
// 3. 如果容量已满,删最老的
if (this.cache.size >= this.maxPages) {
const oldestKey = this.cache.keys().next().value;
this.cache.delete(oldestKey);
}
// 4. 放入缓存,并返回
this.cache.set(pageNo, data);
return data;
}
}
// 用法示例
// 假设 fetchPageFromServer 是 async pageNo => [{ ... }] 的函数
const pager = new LRUCache(5, fetchPageFromServer);
async function loadAndRender(page) {
// 这一行会自动命中则取缓存,未命中则请求并缓存
const items = await pager.get(page);
renderList(items);
}
代码还算好理解
解释下 this.cache.keys().next().value

上图重点是按顺序


所以 this.cache.keys().next().value 拿到最先插入也就是最旧的页面
综上就是我想记录的所有内容了,首先感叹AI真是一名好老师,可以少走很多弯路,欢迎大佬们给出建议,也可以一起讨论你们的开发小技巧