Service Worker 高级缓存策略:预缓存、运行时缓存与更新一致性

HTMLPAGE 团队
14 分钟阅读

从缓存模式选择、资源分层、离线兜底、版本更新到常见事故复盘,系统讲清 Service Worker 在现代前端项目中的高级缓存策略。

#Service Worker #缓存策略 #离线体验 #PWA #前端性能

Service Worker 高级缓存策略

很多团队上 Service Worker,只做了一件事:把静态资源缓存住。结果离线确实能打开页面,但上线后很快遇到新问题:用户一直拿旧版本、接口缓存错乱、修了 bug 却半天生效不了。

这说明缓存不是“存起来就好”,而是一个关于一致性、速度和可恢复性的系统设计。

1. 不同资源类型,必须用不同缓存策略

一套缓存规则打全站,几乎一定会出事。

更稳的分法是:

资源类型推荐策略原因
构建产物 JS/CSSCache First文件名带 hash,可长期复用
页面 HTMLNetwork First保证版本更新及时生效
API 数据Stale While Revalidate 或 Network First兼顾实时性与可用性
图片字体Cache First + 体积限制命中率高,节省流量
离线页Precache网络断开时兜底

Service Worker 的关键不是背策略名,而是明白不同资源的更新风险不同。

2. 预缓存负责“底线可用”,运行时缓存负责“体验变快”

预缓存更适合:

  • app shell
  • 关键字体
  • 离线页
  • 站点 logo 和基础图标

运行时缓存更适合:

  • 用户最近浏览过的内容
  • 图片列表
  • 非关键接口结果

如果你把所有内容都塞进 precache,首装体积会很重;如果完全不做 precache,断网时又没有基本兜底。

3. 一个可维护的实现,不要直接把业务逻辑堆在 fetch 事件里

可以先把策略抽象成函数:

self.addEventListener('fetch', (event) => {
  const url = new URL(event.request.url)

  if (url.pathname.startsWith('/api/')) {
    event.respondWith(networkFirst(event.request, 'api-cache'))
    return
  }

  if (event.request.destination === 'image') {
    event.respondWith(cacheFirst(event.request, 'image-cache'))
    return
  }
})

这样后面做版本升级和问题排查时,不会变成一团不可维护的条件分支。

4. 一个常见失败案例:新版本上线了,老用户却一直拿旧页面

这类事故非常常见:

  1. 首页 HTML 也被 Cache First 了。
  2. 用户第二次访问时直接命中旧缓存。
  3. 新构建已部署,但页面入口没更新。
  4. 用户看到的是“旧 HTML + 新静态资源”混合状态,开始出现奇怪 bug。

根因是把 HTML 当成静态资源长期缓存。

修复方式通常是:

  1. 页面文档改成 Network First。
  2. 使用版本号隔离缓存命名。
  3. 在新 SW 激活时清理旧 cache。

5. 更新策略不能只靠自动替换,要考虑用户正在操作的状态

如果你的产品是内容编辑器、表单、购物车或长表单流程,Service Worker 发现新版本后不能直接粗暴刷新。

更稳的做法是:

  • 新版本准备好时先提示“有更新可用”。
  • 等用户完成当前操作,再触发刷新。
  • 对关键数据先落本地或服务端再切版本。

6. 离线体验不等于“所有功能都能离线”

离线策略的目标应该是:

  • 让用户知道当前网络状态。
  • 保证基础访问和最近内容可读。
  • 告诉用户哪些操作需要恢复联网后继续。

不要为了“支持离线”把所有交互都伪装成成功,这会制造更大的数据一致性问题。

7. 上线前 Checklist

  • 已按 HTML、静态资源、API、图片分开策略。
  • HTML 没有被长期 Cache First 锁死。
  • cache 命名带版本,激活时会清理旧版本。
  • 离线页已预缓存,断网时有明确反馈。
  • 关键写操作不会在断网时伪装成功。
  • 新版本发布时有更新提示与切换机制。
  • 已验证首装体积没有因为 precache 过度膨胀。
  • 已在真实弱网与断网环境做过验证。

FAQ

Service Worker 一定能提升性能吗?

不一定。策略设计错了,反而会让用户长期拿旧资源,或者首装体积更大。

API 数据适合 Cache First 吗?

多数场景不适合。除非数据极稳定,否则更推荐 Network First 或 Stale While Revalidate。

每次发布都要清空全部缓存吗?

不建议。更好的做法是按版本命名并精确清理旧 cache。

延伸阅读