我把91网页版的缓存管理拆给你看:其实一点都不玄学
我把91网页版的缓存管理拆给你看:其实一点都不玄学

开门见山:缓存不是神秘的法术,而是一套可控的策略。对一个典型的网页版应用(像 91 这样的产品)来说,合理的缓存策略能显著提升首屏速度、减少带宽和后端压力,但如果配置不当,会带来用户看到旧内容、登录态异常等问题。下面把缓存按层次拆开,告诉你每层该怎么做、常见坑以及实用代码/命令,拿去直接用。
一、先把“缓存”这几个东西分清楚
- 浏览器 HTTP 缓存(基于响应头:Cache-Control、ETag、Last-Modified)
- CDN / 边缘缓存(s-maxage、缓存键、瞬时清除)
- Service Worker 缓存(Cache Storage API,离线与请求拦截)
- 本地存储(localStorage/sessionStorage/IndexedDB)用于持久化非单源缓存
- 服务器端缓存(Redis、内存缓存)与反向代理缓存(nginx、Varnish)
二、不同内容应该采用的策略(按优先级)
- 静态资源(JS/CSS/图片/字体)
- 长缓存 + 文件指纹(hash)策略:一旦文件名里带 hash,浏览器可以安心缓存很久(例如 max-age=31536000)。
- 示例 header(CDN/服务器返回):Cache-Control: public, max-age=31536000, immutable
- HTML(应用壳 index.html、SSR 页面)
- 短缓存或不缓存:max-age=0, must-revalidate 或 no-cache,结合 ETag/Last-Modified 做条件请求。
- 若使用 Service Worker 做 offline-first,需要严格控制 SW 更新流程,避免用户长期看到旧 HTML。
- API 接口(用户数据、列表等)
- 对于频繁变化的数据用 no-store 或 private, max-age=0,结合 ETag 做条件 GET(减少负载)。
- 对于可短期缓存的数据(如热门列表),可用 Cache-Control: public, max-age=60, stale-while-revalidate=30。
- 登录/个人化内容
- 通常设置 private 或 no-store。把缓存和认证信息的边界划清楚,避免在 CDN 缓存敏感响应。
三、HTTP 缓存关键指令与用法速查(写给工程师的速配表)
- Cache-Control:
- public / private
- max-age=seconds
- s-maxage=seconds(CDN/代理生效)
- no-cache(强制客户端/代理进行条件请求)
- no-store(不允许缓存)
- must-revalidate / proxy-revalidate
- stale-while-revalidate / stale-if-error(现代 CDN 支持)
- ETag / Last-Modified:用于条件请求(If-None-Match / If-Modified-Since),能把 200 响应降为 304,节省流量。
四、静态资源推荐流程(实践)
- 构建时给静态文件名加 hash,例如 main.123abc.js。
- 在构建输出的 HTML 中引入带 hash 的文件名。
- 服务器给这些静态资源下发长缓存头:Cache-Control: public, max-age=31536000, immutable。
- 当发布新版本时,构建会生成新的 hash 文件,旧文件可以按需清理 CDN 缓存。
五、Service Worker(SW)策略与示例 常见策略:
- Precache(安装时缓存关键静态资源)+ Runtime Caching(按需缓存 API 或图片)
- 对于 HTML,一般用 Network First(优先网络,失败回退缓存)以保证页面新鲜度
- 对于图片、字体可以用 Cache First 提升性能
简单 SW 伪代码(核心逻辑): self.addEventListener('install', event => { // 预缓存核心资源 event.waitUntil( caches.open('precache-v1').then(cache => cache.addAll([ '/index.html', '/main.123abc.js', '/styles.456def.css' ])) ); self.skipWaiting(); // 新 SW 安装后尽快激活(配合业务提示刷新) }); self.addEventListener('activate', event => { clients.claim(); // 让新 SW 控制页面 }); self.addEventListener('fetch', event => { const url = new URL(event.request.url); if (url.origin === location.origin && url.pathname.endsWith('.png')) { // 图片:Cache First event.respondWith( caches.match(event.request).then(resp => resp || fetch(event.request).then(res => { let clone = res.clone(); caches.open('images-v1').then(c => c.put(event.request, clone)); return res; })) ); return; } if (event.request.mode === 'navigate') { // HTML 页面:Network First event.respondWith( fetch(event.request).then(res => { let clone = res.clone(); caches.open('pages-v1').then(c => c.put(event.request, clone)); return res; }).catch(() => caches.match('/offline.html')) ); return; } // 其他:默认网络优先 event.respondWith(fetch(event.request).catch(() => caches.match(event.request))); });
注意点:
- 若启用 skipWaiting + clients.claim,请在新版本激活时通知用户刷新页面以拿到新内容,否则用户可能需要手动刷新。
- 小心把 API 的响应误缓存进 SW,尤其是带有用户敏感数据的响应。
六、CDN 与清理(常见业务流程)
- 推荐做法是:静态资源通过 CDN 分发并用 fingerprint;HTML 与动态接口走 origin,并由 CDN 缓存短 TTL 或依据 s-maxage。
- CDN 缓存清理:两种路径
- 按文件名版本(首选):通过文件指纹避免频繁清理。
- API 调用瞬时清除(purge):必要时用 CDN 提供的 API 清除缓存。
- Cache key 要注意 query string 的处理(有些 CDN 会把 ?v=1 当作同一资源,需配置一致性)。
七、服务器示例:nginx 与 express nginx 设置静态长期缓存 location ~* .(js|css|png|jpg|jpeg|gif|svg|webp|woff2?)$ { expires 365d; add_header Cache-Control "public, max-age=31536000, immutable"; }
index.html 或 HTML 页面(短缓存) location / { addheader Cache-Control "no-cache, must-revalidate"; tryfiles $uri /index.html; }
Express ETag 与 Cache-Control 示例 app.use(express.static(path.join(dirname, 'dist'), { maxAge: '1y', // 静态资源 etag: true, lastModified: true })); app.get('/', (req, res) => { res.set('Cache-Control', 'no-cache, must-revalidate'); res.sendFile(path.join(dirname, 'dist', 'index.html')); });
八、调试技巧(快速查问题)
- Chrome DevTools → Network:勾选 Disable cache(开发时禁用浏览器缓存)并观察请求头/响应头。
- Application → Cache Storage:查看 Service Worker 缓存内容。
- curl -I https://yourdomain/path 查看响应头(ETag、Cache-Control)。
- curl -H "If-None-Match:
" -I … 测试 304 行为。 - Lighthouse 查看缓存命中率与重复访问性能得分。
- 日志中统计命中率:CDN 控制台通常给出 cache hit/miss 比例。
九、常见坑与防护
- 把 HTML 缓存太久:用户看不到最新版本。解决:短缓存 + 文件指纹。
- Service Worker 不及时更新:新部署后用户仍然使用旧 SW,需要通过提示或自动激活策略处理。
- CDN 缓存用户数据:确保个人化响应设置 private 或禁用 CDN 缓存。
- API 错误被缓存:避免把 5xx 响应设为长期缓存;可用 stale-if-error 做容错但要慎用。
- 版本控制不一致:前端静态文件 hash 与后端模板不匹配会导致文件加载 404。
十、评估效果:哪些指标能说明缓存做对了
- 首次加载(Cold)与二次加载(Warm)差异:Warm 的 FCP/LCP 大幅下降说明静态资源缓存生效。
- CDN Cache Hit Ratio:目标高命中率(视业务而定)。
- 后端请求量下降:接口调用数减少、数据库压力减轻。
- 用户反馈:刷新频繁或报错明显变少。
十一、针对 91网页版 的推荐落地清单(可复制)
- 构建系统加入文件指纹(JS/CSS/图片/字体)。
- 静态资源设置长缓存并配置 immutable。
- index.html 设置 no-cache,配合 ETag 做条件请求。
- API 重要接口使用 ETag 或短 TTL;登录、个人数据用 private/no-store。
- 若使用 Service Worker:
- 预缓存静态资源,runtime 缓存图片/静态文件;
- 页面使用 Network First;
- 实现更新提示(“发现新版本,点击刷新”)并在用户确认后通过 clients.claim() 和 skipWaiting() 生效。
- CDN 上配置 s-maxage 用于边缘缓存差异化控制,结合按需 purge API 完成紧急回滚。
- 监控:接入 CDN 命中率、Lighthouse 定期检查、后端流量指标。
结语 缓存管理看起来有很多名词和指令,但把它拆成“谁的资源、谁来缓存、缓存多久、如何失效”四个问题来问就不会乱。把静态文件做版本化、把 HTML 保持短缓存、对 API 做策略化缓存,再用 SW 在客户端做补充,整个 91 网页版的缓存体系就既稳又快。需要我把你当前部署的具体请求头或 SW 脚本看一眼并给出精确改法吗?把你现有的响应头或配置贴过来,我可以逐条指出改进点。