Service Worker离线模式失效求助:缓存正常却无法离线运行
Service Worker离线失效及缓存重复问题排查修复
核心问题分析与修复步骤
1. 预缓存与运行时缓存命名冲突
- 问题:代码中
PRECACHE和RUNTIME使用了同一个缓存名precache-v1,导致预缓存资源和运行时缓存资源混存,引发重复缓存实例,还可能覆盖预缓存内容。 - 修复:给
RUNTIME设置独立的缓存名,比如runtime-v1。
2. Install事件未等待预缓存完成
- 问题:
cache.addAll(PRECACHE_URLS)未返回Promise,event.waitUntil无法等待所有资源缓存完成就执行skipWaiting(),导致部分资源可能未缓存成功。 - 修复:在
cache.addAll前添加return,确保Promise链完整,等待缓存操作全部完成。
3. 缓存URL路径不统一
- 问题:
PRECACHE_URLS中混合了相对路径(如/)和绝对路径(如http://localhost:8083/favicon.ico),页面发起的请求多为相对路径,导致caches.match无法匹配到绝对路径缓存的资源。 - 修复:统一使用相对路径,将绝对路径资源改为相对路径,比如
http://localhost:8083/favicon.ico改为/favicon.ico。
4. Service Worker中错误使用navigator对象
- 问题:
navigator.connection.onchange代码放在Service Worker文件中,但Service Worker全局环境没有navigator对象,会抛出错误影响SW正常运行。 - 修复:将这段网络状态监听代码移到页面的JS文件中,而非SW文件。
5. Fetch事件未处理离线请求失败场景
- 问题:当离线且缓存中无匹配资源时,
fetch(event.request)会失败,浏览器会显示默认离线错误页。 - 修复:添加fetch失败的捕获逻辑,返回自定义离线响应或 fallback 资源。
6. 注册代码兼容性判断优化
- 问题:注册代码中
navigator.serviceWorker的判断不够严谨,标准写法应先检查'serviceWorker' in navigator。 - 修复:调整注册条件判断逻辑,同时添加注册成功/失败的日志便于排查。
修改后的Service Worker代码
// 缓存名称:预缓存和运行时缓存分开命名 var PRECACHE = 'precache-v1'; var RUNTIME = 'runtime-v1'; // 统一使用相对路径的预缓存资源列表 var PRECACHE_URLS = [ '/', 'pp-form.js', 'pp-home.js', 'pp.css', '/favicon.ico', '/operation/PO1', '/getForms/PO1/PF1', '/webjars/bootstrap-select/js/bootstrap-select.min.js', '/webjars/bowser/bowser.min.js' ]; self.addEventListener('install', function(event) { console.log('install'); event.waitUntil( caches.open(PRECACHE) .then(function(cache){ // 返回cache.addAll的Promise,确保等待缓存完成 return cache.addAll(PRECACHE_URLS); }) .then(self.skipWaiting()) ); }); self.addEventListener('activate', function(event) { event.waitUntil( caches.keys().then(function(cacheNames){ return Promise.all( cacheNames.map(function(cache){ // 清理非当前版本的预缓存和旧运行时缓存 if (cache !== PRECACHE && cache !== RUNTIME) { return caches.delete(cache); } }) ); }) .then(function () { console.info("Old caches are cleared!"); return self.clients.claim(); }) ); }); self.addEventListener('fetch', function(event) { console.log('fetch'); // 处理同源请求 if (event.request.url.startsWith(self.location.origin)) { event.respondWith( caches.match(event.request).then(function(cachedResponse) { // 缓存命中直接返回 if (cachedResponse) { return cachedResponse; } // 缓存未命中则从网络请求,并存入运行时缓存 return caches.open(RUNTIME).then(function(cache){ return fetch(event.request).then(function(response) { // 仅缓存成功的响应(状态码200+或本地文件状态0) if (response.status === 200 || response.status === 0) { cache.put(event.request, response.clone()); } return response; }).catch(function(error) { // 离线且无缓存时,返回自定义离线响应 console.log('Fetch failed:', error); return new Response('当前离线且该资源未缓存', { status: 503, statusText: 'Service Unavailable' }); }); }); }) ); } });
修改后的注册代码
if ('serviceWorker' in navigator && (location.protocol === 'https:' || location.host.match(/(localhost|127.0.0.1)/))) { navigator.serviceWorker.register('/resources/pp-sw2.js') .then(function(registration) { console.log('Service Worker注册成功:', registration); }) .catch(function(error) { console.log('Service Worker注册失败:', error); }); }
页面中的网络状态监听代码(移至页面JS)
if ('connection' in navigator) { navigator.connection.onchange = function(e) { console.log(navigator.onLine ? "已联网" : "已离线"); }; }
内容的提问来源于stack exchange,提问作者ashwani verma
相关产品推荐
相关产品推荐

