本文作者:V5IfhMOK8g

先别急着下结论,我以为是我要求高,后来才懂91网页版的缓存管理逻辑

V5IfhMOK8g 昨天 45
先别急着下结论,我以为是我要求高,后来才懂91网页版的缓存管理逻辑摘要: 先别急着下结论,我以为是我要求高,后来才懂91网页版的缓存管理逻辑那天我在浏览 91 的网页版,遇到一个让我以为“是我要求太高”的问题:页面内容明明已经上线了新版本,但我刷新好几...

先别急着下结论,我以为是我要求高,后来才懂91网页版的缓存管理逻辑

先别急着下结论,我以为是我要求高,后来才懂91网页版的缓存管理逻辑

那天我在浏览 91 的网页版,遇到一个让我以为“是我要求太高”的问题:页面内容明明已经上线了新版本,但我刷新好几次仍然看到旧的样式、旧的图片,有时候需要清空缓存或者强制刷新才能看到更新。起初我以为是页面做得糟糕、前端工程化不到位,后来把网络请求、Service Worker、CDN 配置彻底捋了一遍,才理解他们背后的缓存管理逻辑——这是为性能和稳定性做的权衡,只是对调试者不够“友好”。

把过程和结论整理一下,既当笔记,也给碰到类似情况的开发者或产品同学一个排查和改进的思路。

我遇到的典型症状

  • 静态资源(js、css、图片)更新后浏览器仍加载旧版本,普通刷新无效,必须强制刷新或清除站点数据。
  • API 数据有缓存行为:有时返回明显旧的数据,过一段时间又恢复正常。
  • 登录态相关页面不一致:有时用户登录后看到的页面是未登录版本,或样式错乱。
  • 在不同网络环境(手机流量 / 公司内网)表现不同。

调查思路与排查步骤(实用) 1) 打开 Chrome DevTools 的 Network 面板,勾选 Disable cache(注意:在 DevTools 打开时生效),看资源的请求头和响应头。 2) 检查响应头:Cache-Control、Expires、ETag、Last-Modified、Vary、Set-Cookie。 3) 看有没有 Service Worker(Application → Service Workers)在拦截请求,并查看它的缓存策略(Cache Storage)。 4) 用 curl -I 检查服务器/CDN 在不同位置的返回头,确认 CDN edge 的缓存策略。 5) 观察资源文件名:是否有 hash(如 main.abc123.js);若有,说明使用了构建指纹化,配合长缓存策略。 6) 在本地复现:修改静态文件、构建后上到测试环境,观察客户端行为,记录不同策略下的结果。

我推断并验证到的缓存策略

  • 长缓存 + 指纹文件名(immutable):对带 hash 的静态资源(如打包后的 js/css/图片),服务器设置 Cache-Control: public, max-age=31536000, immutable;前端通过构建工具(webpack/rollup)生成带 hash 的文件名。优点:极大提升命中率和加载速度;缺点:一旦没有改名,浏览器会一直用旧资源。
  • CDN 层缓存:静态资源由 CDN 缓存,edge 节点的 TTL 与源站头部一致;当资源使用 hash 时,可以放心把 TTL 拉长。更新时通常通过发布触发 CDN 刷新或换文件名。
  • API 与关键页面短缓存或不缓存:对动态内容和需要实时性的 API 通常设置 Cache-Control: no-cache/no-store 或短 TTL,并配合 ETag/Last-Modified 做条件请求,减少不必要流量同时保证一致性。
  • Service Worker 的离线/性能策略:不少站点为提升离线体验或首屏性能,会用 Service Worker 实施 Cache First 或 Stale-While-Revalidate 策略。结果是用户在首次访问后,会从 SW 缓存里快速得到内容;当 SW 规则没有及时更新或 activation 流程被延迟时,会出现“看不到新版本”的情况。
  • 登录态相关的 Vary 和 Cookie:含有 Set-Cookie 或需要鉴权的请求,服务器会通过 Vary: Cookie 或 Authorization 来区分缓存。若误将带有敏感标识的响应缓存到公共 CDN,可能出现用户数据错乱;反之,过度禁用缓存又降低性能。

为什么会“看起来像问题” 这些策略本身合理,但对开发者或某些用户来说“像是缓存没生效”或“页面没更新”。主要原因有:

  • 静态资源用 fingerprint,但页面引用未换名(构建/部署流程问题)。
  • Service Worker 未正确处理 skipWaiting/clients.claim,导致旧 SW长期控制页面。
  • CDN 未及时清除旧缓存,且没有自动化的 cache purge 流程。
  • API 使用了条件缓存,但后端逻辑未更新 ETag/Last-Modified,导致客户端拿到旧数据。

如果你是使用者,如何快速判断并回复最新状态

  • 先试一次“空缓存硬刷新”(Ctrl/Cmd + F5 或 DevTools → Disable cache),看是否更新。
  • 在 DevTools Network 面板观察 response headers,确认是否是浏览器缓存、SW 缓存或 CDN 缓存。
  • 如果怀疑是 Service Worker,打开 Application → Service Workers,尝试 unregister 或更新 SW。

如果你是开发者/运维,建议的改进策略

  • 静态资源:用文件指纹 + 长缓存(Cache-Control: max-age=31536000, immutable)。每次构建确保引用文件名是新的。
  • 动态内容:对 API 使用合理的 Cache-Control + ETag/Last-Modified。需要实时性的接口不要缓存或设置 no-store。
  • Service Worker:设计清晰的更新流程,使用 skipWaiting/clients.claim 并在用户可控的时机提示“有新版本,刷新生效”。避免在默认策略下把重要页面长期缓存。
  • CDN 自动化:构建/部署流水线里加入 CDN 刷新或用版本化路径避免手动 purge。
  • 监控与回滚:发布后监测错误率和用户反馈,必要时能够快速回滚并清理缓存。

收尾感想 从“我要求高”到“理解他们的权衡”,这个过程很有意思。高性能体验往往建立在 agressive 缓存策略之上,但同时需要成熟的构建与发布流程、清晰的缓存策略以及对调试者友好的可观测性。把缓存当成黑盒会让人抓狂;把它当成性能的武器并配以规范的更新机制,就能做到既快又准。

阅读
分享