/* 云笔记 Service Worker — 简单而正确的 PWA 缓存 * 预缓存首页与基础资源;网络优先、失败时回退到缓存(Network-first with cache fallback)。 * 作用域:'/'(由 manifest scope 与 register 的路径共同决定)。 */ 'use strict'; const CACHE_NAME = 'yunjibiji-v1'; const PRECACHE_URLS = [ '/', '/manifest.json', '/icon-192.png', '/icon-512.png', ]; // 安装:预缓存核心资源 self.addEventListener('install', (event) => { event.waitUntil( caches.open(CACHE_NAME) .then((cache) => cache.addAll(PRECACHE_URLS)) .then(() => self.skipWaiting()) ); }); // 激活:清理旧版本缓存 self.addEventListener('activate', (event) => { const cacheWhitelist = [CACHE_NAME]; event.waitUntil( caches.keys().then((cacheNames) => Promise.all( cacheNames.map((cacheName) => { if (!cacheWhitelist.includes(cacheName)) { return caches.delete(cacheName); } }) ) ).then(() => self.clients.claim()) ); }); // 抓取:网络优先,失败时回退到缓存 self.addEventListener('fetch', (event) => { const request = event.request; // 仅处理 GET 请求 if (request.method !== 'GET') return; // 不缓存跨域请求(外部 CDN、图片等),直接放行 const url = new URL(request.url); if (url.origin !== self.location.origin) return; // API 请求不做缓存(保证数据实时) if (url.pathname.startsWith('/api/')) return; // 管理后台不做缓存 if (url.pathname.startsWith('/admin/')) return; event.respondWith( fetch(request) .then((response) => { // 仅缓存有效响应,避免缓存错误页 if (response && response.status === 200 && response.type === 'basic') { const clone = response.clone(); caches.open(CACHE_NAME).then((cache) => cache.put(request, clone)); } return response; }) .catch(() => caches.match(request).then((cached) => { return cached || caches.match('/'); }) ) ); });