日常知识通
柔彩主题三 · 更轻盈的阅读体验

Service Worker 缓存命中率低?几个真实场景帮你揪出问题

发布时间:2026-02-10 11:54:44 阅读:7 次

上周帮朋友优化一个天气小程序,用户反馈“刷新后图标还是旧的”,查了一圈发现 Service Worker 的缓存命中率只有 30% 左右——明明写了缓存逻辑,资源却总绕过缓存走网络。这不是个别现象,很多团队上线 Service Worker 后,看着 DevTools 里 Cache Storage 里一堆缓存项,实际请求却没几个被命中。

缓存命中率 ≠ 缓存已注册

很多人以为 Service Worker 安装成功、cache.addAll() 执行完,缓存就“生效”了。其实不然。比如这个常见写法:

self.addEventListener('fetch', event => {
  if (event.request.destination === 'image') {
    event.respondWith(
      caches.match(event.request).then(res => res || fetch(event.request))
    );
  }
});

表面看没问题,但一旦用户从地址栏直接输入 URL 刷新页面(非导航跳转),浏览器可能发带 Cache-Control: no-cache 的请求头——而 caches.match() 默认不匹配带 vary 或 cache-control 头的请求,结果直接 fallback 到 fetch,缓存形同虚设。

静态资源路径变了,缓存就失效了

前端打包加 hash 是常规操作,比如把 main.js 打成 main.8a3f2b.js。但如果缓存策略写成:

caches.open('v1').then(cache => {
  cache.addAll([
    '/main.js',
    '/style.css'
  ]);
});

那新版本的 main.8a3f2b.js 根本不在缓存里,每次都是网络加载。真正该缓存的是构建后的真实路径,或者用正则+cache.addAll 配合 precache-manifest.json 动态注入。

跳转时的 referrer 也会影响命中

有个电商 H5 页面,商品图在首页点击跳转详情页时能命中缓存,但从微信聊天窗口直接打开详情页却总是重拉图片。查了下请求头,后者带了 Referrer-Policy: strict-origin-when-cross-origin,且请求 URL 末尾多了 ?utm_source=wechat 这类参数。而缓存里存的是无参 URL,caches.match() 默认不忽略查询参数,自然不匹配。

简单补一句就能解决:

const url = new URL(event.request.url);
url.search = ''; // 清空参数再匹配
const cleanRequest = new Request(url.toString(), event.request);
event.respondWith(
  caches.match(cleanRequest).then(r => r || fetch(event.request))
);

缓存命名没做版本隔离,老缓存拖后腿

有次上线新版 SW 脚本,清掉了旧缓存,但忘了改 cacheName,新脚本还在往 v1 里写,而旧页面的 SW 仍控制着同一 cacheName。结果新资源进来了,旧资源又没删干净,caches.match() 在一堆混乱缓存里找,命中率反而更低。后来统一改成 v2-20240521 这种带时间戳的命名,配合 activate 阶段自动清理旧缓存,命中率立刻升到 92%。

缓存命中率不是埋个统计脚本就能看清的事,它藏在每一次 fetch 事件的判断细节里:URL 是否标准化、请求头是否干扰匹配、缓存生命周期是否可控。盯着 Network 面板里的 “from ServiceWorker” 字样看几次,比读十遍文档都管用。