数据请求与缓存策略
数据请求与缓存策略 · 阅读后完成下方实验与检查题,再用「标记完成」记录进度。
学习目标
读完你能实现一个带缓存的最小请求方案:缓存键跟随筛选条件、命中即秒开、未命中带加载态、失败可重试、写入后主动失效;并用来回切换验证「不再闪烁,结果始终与参数对应」。
问题场景
课程库按分类浏览:每次点分类都重新请求,来回切换白屏闪烁;网络抖动一次,页面就停在空状态;改完课程标题回到列表,列表还是旧的。第一版只有一个共享的 isLoading——根本说不清「现在显示的是哪个分类的哪次结果」。
核心解释
最小可用方案 = 以参数为键的缓存 + 每键独立状态机:
const cache = new Map(); // key → { ts, data }
function useCourses(key) {
const [state, setState] = useState(() => cache.get(key) || { data: null });
const [attempt, setAttempt] = useState(0); // 重试计数
useEffect(() => {
let alive = true;
const cached = cache.get(key);
setState(cached ? cached : { data: null }); // 命中:旧数据先行;未命中:加载态
fetchCourses(key)
.then((entry) => {
cache.set(key, entry); // 缓存只存成功结果
if (alive) setState(entry);
})
.catch(() => {
if (alive) setState((s) => ({ ...s, error: true }));
});
return () => { alive = false; }; // 竞态守卫
}, [key, attempt]);
return { ...state, retry: () => setAttempt((a) => a + 1) };
}
缓存键的生成收口成一个函数,参数增减只改一处:
const makeKey = (p) => [p.category, p.page, p.keyword].filter(Boolean).join("|");
四个要点对应四种体验:缓存键必须包含全部影响结果的参数(分类、页码、关键词——漏一个,就会出现「A 分类的数据配 B 分类的标题」,而且因为命中缓存连请求都不发);命中先展示旧数据、后台刷新,来回切换不闪;失败是按键记录的状态,缓存里永远只有成功结果,重试只影响当前键;写操作后主动失效(删除课程后删掉列表缓存或直接更新它),下次读取自然重新请求。验证方式:两个分类来回切换各两次,第二次起应即时显示且数据正确。
示例拆解
实验区课程库有三个分类,请求延迟 500-900ms 不等:
- 首次进入某分类显示「加载中…」;回访直接显示缓存并标注「缓存时间(旧数据先行)」;
- 「断网模式」开关:请求必失败,显示错误态与「重试」按钮;关闭断网后点重试即成功;
- 「强制刷新」按钮:删掉当前键的缓存重新请求,期间旧数据仍然显示;
- 底部实时显示 cache Map 里现存哪些键;
- 验证:切换 全部 → 入门 → 全部 → 进阶 → 全部,确认「全部」的两次回访都即时且正确;断网失败后再恢复网络重试成功。
动手任务
打开本课实验区:
- 给缓存加 30 秒过期:读取时检查 ts,超时当作未命中;验证过期后回访会重新请求。
- 把「强制刷新」改成「失效所有键」,观察底部键列表被清空、各分类下次进入都走加载态。
- 给 makeKey 思路加页码:切换「第 1 页 / 第 2 页」,确认两页各自有缓存、来回切换不闪。
常见错误
- 缓存键漏参数:参数变了键没变,显示错误数据且连请求都不发——最难排查的一类 bug。
- 所有请求共享一个 isLoading:无法表达「A 在加载、B 已就绪」;状态必须按键隔离。
- 失败覆盖了缓存条目:恢复网络后一直显示失败;错误态放 state,缓存只存成功结果。
- 写操作后忘记失效:新增/删除后列表还是旧的;写入成功后删除相关键或用响应更新缓存。
检查与复盘
自查清单:
- 能说出缓存键里必须有哪些参数,为什么少一个就出错
- 切换不闪、失败可重试、写后失效,三条都验证过
- 缓存只存成功结果,错误态与数据分离
参考资料
- React 官方文档:useEffect —— 在 Effect 中请求数据的官方范式与清理约定
- React 官方文档:use —— 配合 Suspense 读取数据的另一条路
- React 官方文档:使用 Effect 进行同步 —— 请求与竞态的底层节奏
读完这一课了?
标记完成后,工作台会安排接下来的学习。