为何SWR在Loader中用CDN缓存比浏览器缓存不阻塞页面加载?
Remix中Cache-Control在浏览器与CDN缓存的行为差异问题
初始loader与headers函数
export async function loader() { const expenses = [ { id: '637f853a8517a37d05c402b5', title: 'first expense', amount: 234, }, ]; // 延迟loader执行以测试缓存效果 await new Promise((res) => { setTimeout(res, 5000); }); console.log('loader is being called'); return json(expenses, { headers: { 'Cache-Control': 'max-age=10, stale-while-revalidate=59', }, }); } export function headers({ loaderHeaders }) { return { 'Cache-Control': loaderHeaders.get('Cache-Control'), }; }
使用max-age时,浏览器会正确缓存10秒,但重新验证阶段loader会被调用,页面因setTimeout阻塞5秒才加载。按预期此时应该能直接返回过期缓存(stale cache),同时后台异步重新验证,但实际页面需要等待loader执行完成,这和stale-while-revalidate的预期不符。
CDN缓存配置代码
改用s-maxage配置CDN缓存的代码如下:
return json(expenses, { headers: { 'Cache-Control': 's-maxage=1, stale-while-revalidate=59', }, });
部署在Vercel上时,stale-while-revalidate工作正常:CDN直接返回过期缓存让页面快速加载,后台异步完成重新验证,完全不受setTimeout的影响。为什么CDN缓存和浏览器缓存的行为会有这种差异?
期望效果
无论使用浏览器缓存还是CDN缓存,都应该返回过期缓存并在后台异步重新验证,页面可以立即(或通过CDN快速)加载。
当前现象
- 使用
max-age+stale-while-revalidate配置浏览器缓存时:max-age生效期间页面加载正常,但重新验证时loader里的setTimeout会阻塞页面加载,这和在loader中使用SWR的初衷相悖(除非我的理解有误)。 - 使用
s-maxage+stale-while-revalidate配置CDN缓存时:s-maxage生效期间页面加载快速,setTimeout不会阻塞页面——CDN返回过期缓存,页面快速加载,这正是我想要的SWR效果,但我希望浏览器缓存也能实现该效果,同时想明白loader不阻塞页面的原因。
已尝试操作
- 在MacBook的不同浏览器及iPhone上测试,浏览器缓存的行为一致。
- 尝试fly、Netlify、Vercel等部署平台,结果相同;本地
npm run dev运行时也是同样现象。 - 新建Remix项目测试,结果一致。
内容的提问来源于stack exchange,提问作者Michael Frieze
相关产品推荐
相关产品推荐

