Skip to main content Skills Marketplace Découvrez et explorez les compétences IA créées par la communauté.
Installer avec Codex ou Claude Copiez ce prompt, collez-le dans Codex, Claude ou un autre assistant, puis laissez-le vérifier la page du skill et l'installer pour vous.
Copier le promptAfficher les détails du prompt Une commande directe contourne le prompt de vérification. Examinez la source avant de l'exécuter.
npx skills add https://github.com/fabioc-aloha/Alex_Plug_In --skill service-worker-offline-firstLa commande reste sur une seule ligne. Faites défiler horizontalement pour la vérifier avant de la copier.
Vous préférez une copie locale ? Téléchargez les fichiers actuellement disponibles dans SkillsMP.
Télécharger Zip Téléchargement... Basé sur la classification professionnelle SOC
name service-worker-offline-first description tier extended Progressive Web App offline-first patterns — Service Worker lifecycle, caching strategies, background sync, and offline fallbacks for resilient web applications applyTo **/*service-worker*,**/*sw.*,**/*offline*,**/*pwa*,**/*cache-strateg*,**/manifest.json
Service Worker Offline-First Patterns
Resilient web applications that work when the network doesn't.
Caching Strategy Selection
Strategy Latency Freshness Best For Cache-First Fastest Stale risk Static assets, fonts, icons Network-First Slower Always fresh API calls, dynamic content Stale-While-Revalidate Fast Eventually fresh Semi-static content, user profiles Network-Only Network-dependent Always fresh Authentication, real-time data Cache-Only Fastest Frozen App shell, offline page
Service Worker Lifecycle
Install → waitUntil(cacheStaticAssets) → skipWaiting()
↓
Activate → waitUntil(cleanOldCaches) → clients.claim()
↓
Fetch → route to strategy by request type
Core Structure const CACHE_VERSION = 'v1' ;
const STATIC_CACHE = `app-static-${CACHE_VERSION} ` ;
const DYNAMIC_CACHE = `app-dynamic-${CACHE_VERSION} ` ;
const API_CACHE = `app-api-${CACHE_VERSION} ` ;
const STATIC_ASSETS = [
'/' ,
'/index.html' ,
'/offline.html' ,
'/manifest.json' ,
];
self.addEventListener ('install' , (event ) => {
event.waitUntil (
caches.open (STATIC_CACHE ).then ((cache ) => cache.addAll (STATIC_ASSETS ))
);
self.skipWaiting ();
});
self.addEventListener ('activate' , (event ) => {
const currentCaches = [STATIC_CACHE , DYNAMIC_CACHE , API_CACHE ];
event.waitUntil (
caches.keys ().then ((keys ) =>
Promise .all (
keys.filter ((key ) => !currentCaches.includes (key))
.map ((key ) => caches.delete (key))
)
)
);
self.clients .claim ();
});
self.addEventListener ('fetch' , (event ) => {
const { request } = event;
if (request.method !== 'GET' ) return ;
const url = new URL (request.url );
if (!url.protocol .startsWith ('http' )) return ;
if (isApiRequest (request)) {
event.respondWith (networkFirstWithTimeout (request, API_CACHE , 5000 ));
} else if (request.destination === 'image' ) {
event.respondWith (cacheFirst (request, STATIC_CACHE ));
} else if (request.mode === 'navigate' ) {
event.respondWith (networkFirstWithTimeout (request, DYNAMIC_CACHE , 3000 ));
} else {
event.respondWith (staleWhileRevalidate (request, DYNAMIC_CACHE ));
}
});
Caching Strategies
Cache-First with Expiry async function cacheFirstWithExpiry (request, cacheName, maxAge ) {
const cache = await caches.open (cacheName);
const cached = await cache.match (request);
if (cached) {
const cachedDate = new Date (cached.headers .get ('sw-cached-date' ) || 0 );
if (Date .now () - cachedDate.getTime () < maxAge) {
return cached;
}
}
try {
const response = await fetch (request);
if (response.ok ) {
const headers = new Headers (response.headers );
headers.append ('sw-cached-date' , new Date ().toISOString ());
const timestamped = new Response (response.clone ().body , {
status : response.status ,
statusText : response.statusText ,
headers,
});
await cache.put (request, timestamped);
}
return response;
} catch {
if (cached) return cached;
throw new Error ('Network failed and no cache available' );
}
}
Network-First with Timeout async function networkFirstWithTimeout (request, cacheName, timeout ) {
const cache = await caches.open (cacheName);
try {
const response = await Promise .race ([
fetch (request),
new Promise ((_, reject ) =>
setTimeout (() => reject (new Error ('Network timeout' )), timeout)
),
]);
if (response.ok ) {
await cache.put (request, response.clone ());
}
return response;
} catch {
const cached = await cache.match (request);
if (cached) return cached;
if (request.mode === 'navigate' ) {
return caches.match ('/offline.html' );
}
return new Response (
JSON .stringify ({ error : 'Offline' , cached : false }),
{ status : 503 , headers : { 'Content-Type' : 'application/json' } }
);
}
}
Stale-While-Revalidate async function staleWhileRevalidate (request, cacheName ) {
const cache = await caches.open (cacheName);
const cached = await cache.match (request);
const fetchPromise = fetch (request).then ((response ) => {
if (response.ok ) cache.put (request, response.clone ());
return response;
});
return cached || fetchPromise;
}
Background Sync Queue failed mutations for retry when connectivity resumes:
self.addEventListener ('sync' , (event ) => {
if (event.tag === 'sync-api-calls' ) {
event.waitUntil (retryFailedRequests ());
}
});
async function retryFailedRequests ( ) {
const queue = await getQueuedRequests ();
const results = await Promise .allSettled (
queue.map ((req ) => fetch (req.url , {
method : req.method ,
headers : new Headers (req.headers ),
body : req.body ,
}))
);
const remaining = queue.filter ((_, i ) =>
results[i].status !== 'fulfilled' || !results[i].value .ok
);
await saveQueuedRequests (remaining);
const clients = await self.clients .matchAll ();
clients.forEach ((client ) => {
client.postMessage ({ type : 'SYNC_COMPLETE' , remaining : remaining.length });
});
}
React Registration if ('serviceWorker' in navigator) {
window .addEventListener ('load' , () => {
navigator.serviceWorker
.register ('/sw.js' )
.then ((registration ) => {
registration.addEventListener ('updatefound' , () => {
const newWorker = registration.installing ;
newWorker?.addEventListener ('statechange' , () => {
if (newWorker.state === 'installed' && navigator.serviceWorker .controller ) {
showUpdateNotification ();
}
});
});
});
});
}
Critical Rules Rule Why Always clean old caches on activate Storage bloats without cleanup Use skipWaiting() + clients.claim() Immediate control of pages Version bump cache names on deploy Forces fresh asset fetch Only cache GET requests Mutations need idempotency guarantees Provide offline fallback for navigation Users see blank page otherwise Test in incognito Avoids stale SW from previous sessions
Common Pitfalls Problem Solution SW serves stale content forever Add cache expiry or version bumping Infinite redirect loops Exclude auth endpoints from caching Cache fills up device storage Set max cache size, prune old entries SW blocks updates Use skipWaiting() to activate immediately API data stale after offline Show "last updated" timestamp to users