Next.js客户端API请求使用memory-cache缓存失效问题
问题原因
你客户端用memory-cache缓存失效的核心原因是:它是内存级缓存,浏览器刷新页面后JS上下文会完全重置,内存里的缓存数据会被清空,所以每次刷新都得重新请求API。
可行解决方案
方案1:用浏览器本地存储(localStorage/sessionStorage)替代内存缓存
这是最直接的改造方案,把缓存数据存在客户端本地,刷新页面也不会丢失,需要自己实现过期逻辑(因为本地存储没有自动过期机制):
修改客户端请求代码:
const [results, setResults] = useState([]); async function getData() { const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone; // 用带时区的key区分不同用户的缓存,避免互相干扰 const cacheKey = `${JSON.stringify(urls)}_${timezone}`; const cachedStr = localStorage.getItem(cacheKey); if (cachedStr) { const cachedData = JSON.parse(cachedStr); // 检查缓存是否在24小时有效期内 if (Date.now() - cachedData.timestamp < 24 * 60 * 60 * 1000) { setResults(cachedData.data); return; } // 过期则删除旧缓存 localStorage.removeItem(cacheKey); } // 无有效缓存时请求API const data = await Promise.all(urls.map(url => fetch(`${url}&timezone=${timezone}`, options).then(resp => resp.json()) )); // 存储缓存,带上时间戳用于后续过期判断 localStorage.setItem(cacheKey, JSON.stringify({ data, timestamp: Date.now() })); setResults(data); } useEffect(() => { getData(); }, []);
注意事项:
- 缓存key必须包含时区,避免不同时区用户的缓存冲突
- 本地存储只能存字符串,所以要通过
JSON.stringify/JSON.parse序列化/反序列化数据 - 手动判断过期时间,超过24小时则触发新请求
方案2:用Service Worker实现HTTP缓存(推荐长期缓存场景)
如果API响应允许缓存,可以用Service Worker拦截请求,实现stale-while-revalidate这类缓存策略——既让用户快速拿到缓存数据,后台又能悄悄更新缓存。Next.js可以通过next-pwa插件快速配置Workbox,简化Service Worker开发:
- 安装
next-pwa依赖 - 在
next.config.js中配置Workbox规则,指定API请求的缓存过期时间为24小时 - 配置完成后,客户端请求API时会自动复用缓存,过期后后台自动更新
方案3:回到服务端渲染,解决时区获取问题
如果不想在客户端处理缓存,也可以改用getServerSideProps,尝试从请求头获取用户时区:
export async function getServerSideProps(req) { // 从请求头获取时区(部分浏览器会发送Accept-Timezone),默认用UTC const timezone = req.headers['accept-timezone'] || 'UTC'; const cacheKey = `${JSON.stringify(urls)}_${timezone}`; const value = cache.get(cacheKey); if (value) { return { props: { result: value } }; } const data = await Promise.all(urls.map(url => fetch(`${url}&timezone=${timezone}`, options).then(resp => resp.json()) )); cache.put(cacheKey, data, 24 * 60 * 60 * 1000); return { props: { result: data } }; }
注意:Accept-Timezone并非所有浏览器都支持,兼容性有限。如果要覆盖所有场景,可以先让客户端加载时把时区通过接口传给服务端,再渲染页面,但会多一次请求开销。
内容的提问来源于stack exchange,提问作者TenB
相关产品推荐
相关产品推荐

