Comprensione e utilizzo dei lavoratori in JavaScript per le capacità offline

I dipendenti del servizio sono la base di questa aspettativa, consentendo a Progressive Web Apps (PWAs) di lavorare offline, caricare rapidamente su connessioni infuocate e fornire notifiche push. A differenza delle tecniche di cache tradizionali, un Service Worker agisce come un proxy programmabile seduto tra il browser e la rete.

I Service Workers non sono una nuova tecnologia, sono stati supportati nei principali browser per diversi anni, ma rimangono sottoutilizzati da molti team di sviluppo. Quando sono stati costruiti correttamente, trasformano un'app web statica in un'esperienza resiliente e app-like. Passeremo attraverso il ciclo di vita completo, discuteremo strategie di cache che si adattano a diversi tipi di contenuti, e coprire API di compagno come Background Sync e Push Notifications.

Cos'è un Service Worker?

Un Service Worker è un file JavaScript che viene eseguito in un ambito globale separato dalla tua pagina web, sul suo filetto. Non ha accesso DOM e comunica con la tua pagina solo attraverso []. Il suo lavoro primario è quello di agire come intermediario per le richieste di rete: il browser può inviare qualsiasi richiesta dalla tua pagina web al Service Worker, che poi decide come rispondere - dalla rete, da una cache, o costruendo una risposta personalizzata.

Poiché funziona in background, un Service Worker può continuare a funzionare anche dopo che l'utente chiude la pagina che la registra.

  • Supporto per la linea [[] – servire pagine memorizzate nella cache quando la rete non è disponibile.
  • I dati di sfondo sincronizzazione[] – le azioni di queuing mentre offline e inviarle quando la connettività ritorna.
  • Notifiche di Pissure[[] – ricevere messaggi da un server e visualizzarli all'utente anche quando l'applicazione non è aperta.
  • Ottimizzazione delle prestazioni[] – attività critiche pre-caching in modo che l'applicazione carica istantaneamente sulle visite ripetute.

I Service Workers si affidano a un requisito di sicurezza rigoroso: lavorano solo sotto HTTPS (o su per lo sviluppo), proteggendo gli utenti dagli attacchi di mezzo che potrebbero manomettere lo script.

Il ciclo di vita del lavoratore di servizio

Un Service Worker procede attraverso un ciclo di vita ben definito: registrazione, installazione, attivazione e quindi gestione inattivo/fetch. Capire questo ciclo di vita è essenziale perché determina quando le risorse memorizzate diventano disponibili e quando le vecchie cache vengono ripulite.

1. Registrazione

Prima che qualsiasi logica di Service Worker possa essere eseguita, la tua pagina deve registrare lo script. Questo viene fatto dal tuo file JavaScript principale (di solito il punto di entrata della pagina). La registrazione informa il browser della posizione e dell'ambito del Service Worker. L'ambito definisce quali URL può controllare il Service Worker; di default è la directory dello script.

if ('serviceWorker' in navigator) {
 navigator.serviceWorker.register('/service-worker.js')
 .then(function(registration) {
 console.log('Service Worker registered with scope:', registration.scope);
 })
 .catch(function(error) {
 console.log('Registration failed:', error);
 });
}

Se lo script esiste già, il browser confronta il contenuto byte‐for‐byte con la versione precedentemente installata. Se è cambiato, una nuova versione viene installata sullo sfondo mentre la vecchia versione continua a controllare le pagine.

2. Installazione

L'evento è il primo evento che riceve il tuo Service Worker: è il momento perfetto per pre-cache i file essenziali che la tua app deve lavorare offline: la shell app, core CSS, JavaScript e qualsiasi pagina di fallback.

const CACHE_NAME = 'my-cache-v1';
const urlsToCache = [
 '/',
 '/styles/main.css',
 '/scripts/main.js',
 '/offline.html'
];

self.addEventListener('install', function(event) {
 event.waitUntil(
 caches.open(CACHE_NAME)
 .then(function(cache) {
 console.log('Opened cache');
 return cache.addAll(urlsToCache);
 })
 );
});

Se uno dei file non riesce a scaricare durante [], il passaggio di installazione non viene considerato installato e il Service Worker non viene considerato installato.

3. Attivazione

Dopo l'installazione, il browser attende fino a quando tutte le pagine caricate con il vecchio Service Worker sono chiuse. Poi accende l'evento [. Questo evento è tipicamente utilizzato per pulire vecchie cache e per prendere il controllo di qualsiasi pagina che è stata aperta prima dell'attivazione.

self.addEventListener('activate', function(event) {
 const cacheWhitelist = [CACHE_NAME];
 event.waitUntil(
 caches.keys().then(function(cacheNames) {
 return Promise.all(
 cacheNames.map(function(cacheName) {
 if (cacheWhitelist.indexOf(cacheName) === -1) {
 return caches.delete(cacheName);
 }
 })
 );
 })
 );
});

Chiamare all'interno del ] il manubrio prende immediatamente il controllo di tutte le pagine aperte senza richiedere un ricaricamento.

4. Fetch Eventi

Una volta attivato, il Service Worker ascolta gli eventi ]. Ogni volta che il browser avvia una richiesta da una pagina controllata, il Service Worker può intercettarlo.

self.addEventListener('fetch', function(event) {
 event.respondWith(
 caches.match(event.request)
 .then(function(response) {
 // Cache hit - return response
 if (response) {
 return response;
 }
 return fetch(event.request);
 }
 )
 );
});

Strategie di cache per diversi scenari

La scelta della giusta strategia di caching determina l'equilibrio tra freschezza e velocità, non esiste una soluzione completa di taglia unica; le diverse risorse richiedono approcci diversi.

Cache‐First (Cache poi Network)

Meglio per le attività statiche che raramente cambiano: immagini, font, CSS. Il Service Worker controlla prima la cache; se trovato, ritorna immediatamente. In caso contrario, si fetches dalla rete e memorizza la risposta. Questa strategia produce carichi ripetitivi veloci di fulmine.

self.addEventListener('fetch', function(event) {
 event.respondWith(
 caches.match(event.request)
 .then(function(response) {
 return response || fetch(event.request).then(function(networkResponse) {
 return caches.open(CACHE_NAME).then(function(cache) {
 cache.put(event.request, networkResponse.clone());
 return networkResponse;
 });
 });
 })
 );
});

Network‐First (Rete con Cache Fallback)

Ideale per contenuti dinamici: endpoint API, news feed. Prova prima la rete; se fallisce o è molto lento, torna alla versione cache. Questo garantisce agli utenti di vedere sempre i dati più recenti a meno che non siano offline.

self.addEventListener('fetch', function(event) {
 event.respondWith(
 fetch(event.request)
 .then(function(networkResponse) {
 return caches.open(CACHE_NAME).then(function(cache) {
 cache.put(event.request, networkResponse.clone());
 return networkResponse;
 });
 })
 .catch(function() {
 return caches.match(event.request);
 })
 );
});

Stale-While-Revalidate

Il meglio per le risorse che non devono essere immediatamente fresche: avatar di profilo, feed RSS.Risponde immediatamente alla versione cache, ma anche prendere un aggiornamento dalla rete in background. La prossima volta che la risorsa è richiesta, viene servita la nuova voce cache.

self.addEventListener('fetch', function(event) {
 event.respondWith(
 caches.open(CACHE_NAME).then(function(cache) {
 return cache.match(event.request).then(function(cachedResponse) {
 var fetchPromise = fetch(event.request).then(function(networkResponse) {
 cache.put(event.request, networkResponse.clone());
 return networkResponse;
 });
 return cachedResponse || fetchPromise;
 });
 })
 );
});

Rete-Only e Cache-Only

Per dati sensibili come i dettagli dell'utente bancari, utilizzare Network-Only (senza cache). Per contenuti statici puramente offline (ad esempio, un manuale), Cache-Only può essere sufficiente.

Caratteristiche avanzate del lavoro di servizio

Oltre al caching, i lavoratori di servizio sbloccano potenti capacità di sfondo.

Sincronizzazione dello sfondo

Quando un utente invia un modulo o invia un messaggio mentre non è offline, non si desidera perdere quell'azione. ] consente di registrare un evento di sincronizzazione che il browser accenderà una volta che la connettività è tornata.

// In page script
navigator.serviceWorker.ready.then(function(registration) {
 return registration.sync.register('sync-messages');
});

// In Service Worker
self.addEventListener('sync', function(event) {
 if (event.tag === 'sync-messages') {
 event.waitUntil(sendPendingMessages());
 }
});

Background Sync è ideale per le applicazioni di chat, sottomissioni di form o qualsiasi coda di dati che necessita di una consistenza.

Spingere le notifiche

I messaggi push arrivano da un server e svegliano il Service Worker, che poi visualizza una notifica. Questo funziona anche quando la tua app web è chiusa, dando a PWAs un'atmosfera nativo.

self.addEventListener('push', function(event) {
 const options = {
 body: 'This notification was triggered by a push message.',
 icon: '/icons/icon-192x192.png',
 badge: '/icons/badge.png',
 data: {
 url: '/new-content'
 }
 };
 event.waitUntil(
 self.registration.showNotification('New Update', options)
 );
});

Fare clic sulla notifica può aprire un URL specifico utilizzando l'evento .

Sicurezza e Scope

I Service Workers possono intercettare e modificare le richieste, quindi devono essere serviti su HTTPS per evitare che gli script dannosi vengano iniettati. Durante lo sviluppo locale, i browser consentono ai Service Workers ] come eccezione.

Per impostazione predefinita, l’ambito è la directory in cui si trova lo script Service Worker. Per ampliare l’ambito (ad esempio, controllare tutte le pagine in ), è possibile impostare l’opzione ] durante la registrazione. È inoltre necessario includere un script di estensione ] .

Debugging lavoratori del servizio

In Chrome, aperto o andare a Applicazione → Service Workers. È possibile vedere lo stato di ogni lavoratore, avviarli manualmente / fermarli, e le registrazioni chiare. Firefox offre strumenti simili in circa:debugging.

Consigli di debug comuni:

  • Aggiornare su ricarica[[] – in Chrome DevTools, controllare “Aggiornare su ricarica” in modo che un nuovo Service Worker venga installato ogni volta che la pagina ricarica (utile durante lo sviluppo).
  • Bypass per la rete[[] – disattiva temporaneamente il Service Worker per vedere come la tua app si comporta senza di essa.
  • Cache storage[[] – ispezionare le risorse cache nel pannello di memorizzazione Cache per verificare la strategia di cache.
  • Logging[] – utilizzare [ all'interno del Service Worker; i messaggi appaiono nella console di sfondo in DevTools.

Implicazioni di performance

I Service Workers possono migliorare notevolmente le prestazioni percepite, ma anche introdurre la complessità. Pre-caching troppe risorse durante l'installazione può ritardare il passo `install` e causare il fallimento dell'installazione se si verificano timeout. Una buona pratica è quella di nascondere solo la shell minima in e lazily cache risorse aggiuntive durante gli eventi di fetch.

La dimensione della cache è limitata per origine (tipicamente poche centinaia di megabyte sui browser mobili). Per monitorare l'utilizzo, è possibile inserire i file multimediali di grandi dimensioni.

Un'altra trappola per prestazioni: se il tuo Service Worker esegue un calcolo pesante all'interno del manubrio [, può bloccare o ritardare le risposte di rete.

Pitfalls comune e come evitare di loro

  • I contenuti delle statistiche[[]] – sempre la versione delle cache (ad esempio [], ) ed eliminano le vecchie cache durante l'evento .
  • Pagina di inconveniente non registrata[[] – se la tua pagina offline non è pre-cached, gli utenti vedranno una pagina di errore del browser.
  • Broken POST request[[] – I lavoratori possono intercettare le richieste POST, ma dovete stare attenti con il caching. ] non supporta POST per impostazione predefinita; gestirle separatamente o semplicemente passarle attraverso.
  • ]Missing [] – se non si chiama [, il browser cercherà di prendere la risorsa da solo, potenzialmente bypassando la vostra logica di cache.

Real‐World Esempio: un semplice Offline‐Ready PWA

Mettiamo tutto insieme. Creeremo un piccolo PWA che memorizza il suo guscio e fornisce un fallback offline. Il file Service Worker completo ():

const CACHE_NAME = 'pwa-shell-v2';
const SHELL_URLS = [
 '/',
 '/index.html',
 '/styles.css',
 '/app.js',
 '/offline.html'
];

// Install: pre‑cache shell
self.addEventListener('install', function(event) {
 event.waitUntil(
 caches.open(CACHE_NAME)
 .then(function(cache) {
 return cache.addAll(SHELL_URLS);
 })
 );
});

// Activate: clean old caches
self.addEventListener('activate', function(event) {
 const cacheWhitelist = [CACHE_NAME];
 event.waitUntil(
 caches.keys().then(function(cacheNames) {
 return Promise.all(
 cacheNames.map(function(name) {
 if (!cacheWhitelist.includes(name)) {
 return caches.delete(name);
 }
 })
 );
 }).then(function() {
 return self.clients.claim();
 })
 );
});

// Fetch: network‑first for pages, cache‑first for static assets
self.addEventListener('fetch', function(event) {
 if (event.request.mode === 'navigate') {
 // Pages: try network, fall back to cache
 event.respondWith(
 fetch(event.request).then(function(networkResponse) {
 // Update cache with fresh page
 const responseClone = networkResponse.clone();
 caches.open(CACHE_NAME).then(function(cache) {
 cache.put(event.request, responseClone);
 });
 return networkResponse;
 }).catch(function() {
 return caches.match(event.request).then(function(cached) {
 // If page not cached, show offline fallback
 return cached || caches.match('/offline.html');
 });
 })
 );
 } else {
 // Static assets: cache‑first
 event.respondWith(
 caches.match(event.request).then(function(cached) {
 return cached || fetch(event.request).then(function(networkResponse) {
 caches.open(CACHE_NAME).then(function(cache) {
 cache.put(event.request, networkResponse.clone());
 });
 return networkResponse;
 });
 })
 );
 }
});

Questo esempio utilizza un approccio di rete-first per la navigazione (le pagine aggiornate quando non sono online, offline fallback) e cache-first per tutti gli altri asset.

Conclusioni

I Service Workers sono la spina dorsale delle moderne applicazioni web offline, che offrono uno strato di rete programmabile che va ben oltre il semplice caching: sincronizzazione di sfondo, notifiche push e controllo completo su come vengono servite le risorse.

Per immergersi più in profondità nelle API, consultare la documentazione ufficiale []MDN e la Google Web Basics[]] guida. Per i modelli di caching avanzati, il Offline Cookbook]] di Jake Archibald offre ricette dettagliate.