/* ===================================================== * 萌芽密码管理器 Service Worker * 策略: * - 静态资源(Shell): Cache First(优先缓存) * - API 请求: Network First(优先网络,失败时返回离线页) * - 导航请求: Network First → 回退到缓存的 index.html * ===================================================== */ const CACHE_NAME = 'mengyakeyvault-v1'; const OFFLINE_URL = '/offline.html'; // 预缓存的应用 Shell 资源 const PRECACHE_URLS = [ '/', '/index.html', '/offline.html', '/manifest.json', '/favicon.ico', '/logo.png', ]; // ── Install ────────────────────────────────────────── self.addEventListener('install', (event) => { event.waitUntil( caches.open(CACHE_NAME).then((cache) => { return cache.addAll(PRECACHE_URLS).catch((err) => { console.warn('[SW] 预缓存部分资源失败:', err); }); }) ); // 强制新 SW 立即激活,不等旧 SW 退出 self.skipWaiting(); }); // ── Activate ───────────────────────────────────────── self.addEventListener('activate', (event) => { event.waitUntil( caches.keys().then((cacheNames) => { return Promise.all( cacheNames .filter((name) => name !== CACHE_NAME) .map((name) => caches.delete(name)) ); }) ); // 立即接管所有页面 self.clients.claim(); }); // ── Fetch ───────────────────────────────────────────── self.addEventListener('fetch', (event) => { const { request } = event; const url = new URL(request.url); // 只处理 http/https,忽略 chrome-extension 等 if (!url.protocol.startsWith('http')) return; // API 请求:Network First if (url.pathname.startsWith('/api') || url.hostname.includes('keyvault.api')) { event.respondWith(networkFirst(request)); return; } // 第三方资源(favicon API 等):Network First,不缓存 if (url.hostname !== self.location.hostname) { event.respondWith(networkOnly(request)); return; } // 导航请求(HTML页面):Network First → 回退 index.html if (request.mode === 'navigate') { event.respondWith(navigationHandler(request)); return; } // 静态资源(JS/CSS/图片等):Cache First event.respondWith(cacheFirst(request)); }); // ── 策略函数 ────────────────────────────────────────── // Cache First:先查缓存,没有再请求网络并写入缓存 async function cacheFirst(request) { const cached = await caches.match(request); if (cached) return cached; try { const response = await fetch(request); if (response.ok) { const cache = await caches.open(CACHE_NAME); cache.put(request, response.clone()); } return response; } catch { return new Response('资源暂时无法访问', { status: 503 }); } } // Network First:先请求网络,失败时查缓存 async function networkFirst(request) { try { const response = await fetch(request); if (response.ok) { const cache = await caches.open(CACHE_NAME); cache.put(request, response.clone()); } return response; } catch { const cached = await caches.match(request); return cached || new Response( JSON.stringify({ error: '网络不可用,请检查连接' }), { status: 503, headers: { 'Content-Type': 'application/json' } } ); } } // Network Only:仅网络,不缓存 async function networkOnly(request) { try { return await fetch(request); } catch { return new Response('', { status: 503 }); } } // 导航处理:Network First → 回退缓存的 index.html async function navigationHandler(request) { try { const response = await fetch(request); if (response.ok) { const cache = await caches.open(CACHE_NAME); cache.put(request, response.clone()); } return response; } catch { const cached = await caches.match('/index.html'); if (cached) return cached; return caches.match(OFFLINE_URL); } } // ── 消息处理(支持主线程主动触发更新)────────────────── self.addEventListener('message', (event) => { if (event.data && event.data.type === 'SKIP_WAITING') { self.skipWaiting(); } });