sw.js (2028B)
1 /* Service Worker — caderno de estudos 2 Estratégia: cache-first para assets estáticos, network-first para HTML 3 Versão: 3 (com suporte a stale-while-revalidate) */ 4 const CACHE = 'caderno-v3'; 5 const STATIC = [ 6 '/', 7 '/offline.html' 8 ]; 9 10 self.addEventListener('install', e => { 11 e.waitUntil( 12 caches.open(CACHE).then(c => c.addAll(STATIC)).then(() => self.skipWaiting()) 13 ); 14 }); 15 16 self.addEventListener('activate', e => { 17 e.waitUntil( 18 caches.keys().then(keys => 19 Promise.all(keys.filter(k => k !== CACHE).map(k => caches.delete(k))) 20 ).then(() => self.clients.claim()) 21 ); 22 }); 23 24 self.addEventListener('fetch', e => { 25 const url = new URL(e.request.url); 26 27 // Ignora requisições externas (fontes Google, etc.) 28 if (url.origin !== location.origin) return; 29 30 if (e.request.mode === 'navigate') { 31 // HTML: network-first, fallback para offline.html 32 e.respondWith( 33 fetch(e.request) 34 .then(res => { 35 const clone = res.clone(); 36 caches.open(CACHE).then(c => c.put(e.request, clone)); 37 return res; 38 }) 39 .catch(() => caches.match(e.request).then(r => r || caches.match('/offline.html'))) 40 ); 41 } else { 42 // Assets (CSS, JS, imagens): stale-while-revalidate 43 e.respondWith( 44 caches.match(e.request).then(cached => { 45 // Se estiver em cache, retorna imediatamente e atualiza em background 46 if (cached) { 47 // Atualizar em background 48 fetch(e.request).then(res => { 49 if (res && res.status === 200) { 50 const clone = res.clone(); 51 caches.open(CACHE).then(c => c.put(e.request, clone)); 52 } 53 }); 54 return cached; 55 } 56 57 // Se não estiver em cache, busca da rede 58 return fetch(e.request).then(res => { 59 if (res && res.status === 200) { 60 const clone = res.clone(); 61 caches.open(CACHE).then(c => c.put(e.request, clone)); 62 } 63 return res; 64 }); 65 }) 66 ); 67 } 68 });