/** * Service Worker — 今天吃啥 PWA * 策略:静态资源 Cache First,数据 Network First,图片 Cache First */ const CACHE_STATIC = 'weinan-dishes-v1'; const CACHE_IMAGES = 'weinan-images-v1'; const STATIC_FILES = [ '/', '/index.html', '/style.css', '/app.js', '/dishes.json', '/manifest.json', ]; // Install: 预缓存核心静态资源 self.addEventListener('install', (event) => { event.waitUntil( caches.open(CACHE_STATIC).then((cache) => { return cache.addAll(STATIC_FILES); }) ); self.skipWaiting(); }); // Activate: 清理旧缓存 self.addEventListener('activate', (event) => { event.waitUntil( caches.keys().then((keys) => { return Promise.all( keys.filter((k) => k !== CACHE_STATIC && k !== CACHE_IMAGES) .map((k) => caches.delete(k)) ); }) ); self.clients.claim(); }); // Fetch: 按资源类型分策略 self.addEventListener('fetch', (event) => { const url = new URL(event.request.url); // 图片: Cache First if (url.pathname.match(/\.(jpg|png|svg|webp|ico)$/i)) { event.respondWith( caches.match(event.request).then((cached) => { const fetchPromise = fetch(event.request).then((response) => { if (response.ok) { const clone = response.clone(); caches.open(CACHE_IMAGES).then((cache) => cache.put(event.request, clone)); } return response; }); return cached || fetchPromise; }) ); return; } // dishes.json: Network First(保证数据新鲜) if (url.pathname.endsWith('dishes.json')) { event.respondWith( fetch(event.request) .then((response) => { const clone = response.clone(); caches.open(CACHE_STATIC).then((cache) => cache.put(event.request, clone)); return response; }) .catch(() => caches.match(event.request)) ); return; } // HTML/CSS/JS: Cache First(App Shell) event.respondWith( caches.match(event.request).then((cached) => { const fetchPromise = fetch(event.request).then((response) => { if (response.ok) { const clone = response.clone(); caches.open(CACHE_STATIC).then((cache) => cache.put(event.request, clone)); } return response; }); return cached || fetchPromise; }) ); });