Service Worker无法激活问题排查:缓存更新与事件触发异常
看起来你的Service Worker遇到了生命周期和事件处理的核心问题,我来逐一拆解并给出修复方案:
核心问题点
1. Service Worker激活事件仅在文件更新时触发
浏览器的Service Worker机制是:只有当serviceWorker.js文件内容发生实质性变化(哪怕是一行注释的修改)时,才会触发新的安装→激活流程。你当前的代码中,每次访问index.html注册的都是同一个未修改的serviceWorker.js,所以第二次及之后访问时,浏览器认为Worker已经处于激活状态,不会重复触发activate事件,自然不会执行缓存版本检查的逻辑。
2. fetch事件未正确使用event.respondWith()
你的fetch事件处理函数犯了一个关键错误:没有调用event.respondWith()来包裹响应逻辑。Service Worker对请求的拦截必须通过这个方法来返回自定义响应(缓存或网络请求),否则浏览器会默认中断请求,导致后续请求无响应。另外,cache变量仅在activate事件中初始化,如果activate没触发,cache会是undefined,执行cache.match()时会直接抛出错误,进一步破坏后续的fetch事件处理。
3. 缓存清理逻辑有误
你遍历缓存的方式caches.length并不是有效的API,正确获取所有缓存名称的方式是await caches.keys(),否则无法正确识别并删除旧缓存。
修复方案
步骤1:强制触发Service Worker更新检查
在页面注册Worker时,添加手动更新检查逻辑,确保每次页面加载时都检测是否有新版本:
window.addEventListener('load', async function(){ if ( 'serviceWorker' in navigator ) { try{ var registration=await navigator.serviceWorker.register('serviceWorker.js'); // 监听更新事件 registration.addEventListener('updatefound', () => { const newWorker = registration.installing; newWorker.addEventListener('statechange', () => { if (newWorker.state === 'installed') { if (navigator.serviceWorker.controller) { console.log('Nouvelle version disponible, rafraîchissez la page'); } } }); }); // 手动触发更新检查 await registration.update(); console.log('ServiceWorker enregistré avec le scope: ', registration.scope); document.getElementById('worker').innerHTML='serviceWorker.js actif'; document.getElementById('cache').innerHTML='Cache actif'; }catch (err){ console.log(err); } }else{window.alert('Votre navigateur ne supporte pas les Service Workers. Ce site ne fonctionnera pas en mode offline');}; });
步骤2:修正Service Worker的激活逻辑
修复缓存清理逻辑,并确保缓存名称在全局作用域存储:
let cacheName; // 全局存储当前缓存版本名称 self.addEventListener('install', event => { self.skipWaiting(); console.log("ServiceWorker installé"); }); self.addEventListener('activate', async function(event){ self.clients.claim(); console.log("ServiceWorker activé"); if (navigator.onLine){ try { const oReq = new Request('version.txt?date='+new Date().getTime(), {method:'get'}); const response = await fetch(oReq); cacheName = await response.text(); console.log('Nouvelle version du cache: '+cacheName); // 获取所有缓存名称并清理旧缓存 const existingCaches = await caches.keys(); await Promise.all( existingCaches.map(cache => { if (cache !== cacheName) { return caches.delete(cache); } }) ); // 打开当前缓存并添加资源 const cache = await caches.open(cacheName); await cache.addAll(['index.html','styles.css','traitement.js']); } catch (err) { console.log('Erreur lors de l\'activation:', err); } } });
步骤3:修复fetch事件处理逻辑
必须用event.respondWith()包裹响应逻辑,同时处理缓存未初始化的情况:
self.addEventListener('fetch', function(event){ console.log('Traitement du fetch '+event.request.url); if(event.request.url.includes('version.txt')){ console.log('On laisse passer '+event.request.url); event.respondWith(fetch(event.request)); }else{ event.respondWith( (async function() { // 如果缓存未初始化,直接走网络请求 if (!cacheName) { return fetch(event.request); } try{ const cache = await caches.open(cacheName); const cachedResponse = await cache.match(event.request); if (cachedResponse){ console.log('on sert '+event.request.url+' depuis le cache'); return cachedResponse; } // 缓存无资源时走网络,可选:将新请求加入缓存 const networkResponse = await fetch(event.request); await cache.put(event.request, networkResponse.clone()); return networkResponse; }catch(err){ console.log(event.request.url+' not cached or error:', err); // 离线且无缓存时返回错误提示 return new Response('Erreur de chargement hors ligne', {status: 500}); } })() ); } });
额外提示
- 测试时,若要触发新的激活流程,可以在
serviceWorker.js中添加一行无关注释(修改文件内容),这样浏览器会认为是新版本。 navigator.onLine的判断并不完全可靠,建议在网络请求失败时再降级到离线逻辑,而非仅依赖这个属性。
内容的提问来源于stack exchange,提问作者eddie

