// PWA Service Worker v3 // - /api /auth /admin /sync 一律直连,绝不缓存(修复切换账号后 /auth/me 命中旧缓存的问题) // - 仅指纹化静态资源(/assets/、/icons/)缓存优先 // - 页面导航网络优先,离线回落缓存的 index.html const CACHE = 'fp-static-v3'; const CORE = ['/manifest.webmanifest', '/icons/pwa-192.png', '/icons/pwa-512.png']; self.addEventListener('install', (e) => { self.skipWaiting(); e.waitUntil(caches.open(CACHE).then((c) => c.addAll(CORE))); }); self.addEventListener('activate', (e) => { e.waitUntil( caches.keys() .then((ks) => Promise.all(ks.filter((k) => k !== CACHE).map((k) => caches.delete(k)))) .then(() => self.clients.claim()) ); }); self.addEventListener('fetch', (e) => { const url = new URL(e.request.url); if (e.request.method !== 'GET') return; // 后端接口直连(含 /api /auth /admin /sync),绝不缓存 if (/^\/(api|auth|admin|sync)(\/|$)/.test(url.pathname)) return; // 指纹化静态资源:缓存优先(内容不可变) if (url.pathname.startsWith('/assets/') || url.pathname.startsWith('/icons/')) { e.respondWith( caches.match(e.request).then((hit) => hit || fetch(e.request).then((res) => { if (res.ok && url.origin === location.origin) { const copy = res.clone(); caches.open(CACHE).then((c) => c.put(e.request, copy)); } return res; })) ); return; } // 页面导航:网络优先,离线回落 if (e.request.mode === 'navigate') { e.respondWith( fetch(e.request).then((res) => { const copy = res.clone(); caches.open(CACHE).then((c) => c.put('/index.html', copy)); return res; }).catch(() => caches.match('/index.html').then((h) => h || caches.match('/'))) ); return; } // 其余(manifest 等):网络直连 });