PWA离线缓存为什么没生效?

迷人的宇硕 阅读 3

我按照教程给项目加了 Service Worker 和 Cache API,但刷新页面后还是没法离线访问,控制台也没报错,到底哪里出问题了?

我注册了 sw.js,在 install 事件里缓存了首页和关键资源,但断网后再打开页面就直接显示“无网络连接”,而不是加载缓存的内容。

self.addEventListener('install', (event) => {
  event.waitUntil(
    caches.open('v1').then((cache) => {
      return cache.addAll([
        '/',
        '/index.html',
        '/styles/main.css',
        '/scripts/app.js'
      ]);
    })
  );
});

路径应该没错,本地开发时用的是 http-server 启的,不是 file:// 协议。是不是 fetch 事件没处理?但我以为 install 缓存了就能自动用……

我来解答 赞 2 收藏
二维码
手机扫码查看
1 条解答
东方珮青
你的问题很典型:缓存是存好了,但没人去取啊。

Service Worker 的 install 事件只是负责把资源缓存起来,但它不会自动拦截请求并从缓存里返回。你需要在 fetch 事件里显式地告诉浏览器:“遇到请求时,先看看缓存里有没有,有就直接用缓存,没有再去网络请求”。

你的代码缺的就是这部分:

// 拦截 fetch 请求
self.addEventListener('fetch', (event) => {
event.respondWith(
caches.match(event.request).then((response) => {
// 缓存命中就直接返回
if (response) {
return response;
}
// 缓存没命中,去网络请求
return fetch(event.request);
})
);
});
加上这段之后,断网刷新应该就能看到缓存的内容了。

再补充几点可能遇到的坑:

浏览器缓存可能捣乱。如果你用 http-server 本地开发,浏览器可能直接用了自身的 HTTP 缓存,根本没走到 Service Worker。可以在 DevTools 的 Application 面板看看 Service Worker 状态是不是 "activated and running",Network 里请求的 Size 栏显示的是 "from cache" 还是 "from ServiceWorker"。

跨域资源要注意。缓存外部资源(比如 CDN 上的字体、图片)时,Cache API 默认只能缓存同源资源。如果要缓存跨域资源,需要在 fetch 时加上 <code>mode: 'no-cors'</code>,但这样缓存到的响应会是 opaque 响应(body 为空),只能用来判断存在不存在,不能读取内容。

activate 阶段最好清理旧缓存,不然版本更新时会出问题:

<pre class="pure-highlightjs line-numbers"><code class="language-javascript">self.addEventListener('activate', (event) => {
event.waitUntil(
caches.keys().then((cacheNames) => {
return Promise.all(
cacheNames.map((cacheName) => {
// 只删除旧版本的缓存
if (cacheName !== 'v1') {
return caches.delete(cacheName);
}
})
);
})
);
});


最后记得在 DevTools 的 Service Worker 面板勾选 "Update on reload",开发时能省很多事。
点赞
2026-03-12 16:02