`Service Worker` in JavaScript
Questo articolo spiega il concetto di Service Worker
in JavaScript.
Spiegheremo passo dopo passo dalle basi dei Service Worker
al controllo pratico della cache.
YouTube Video
offline.html
1<!DOCTYPE html>
2<html lang="en">
3<head>
4 <meta charset="UTF-8">
5 <title>Offline</title>
6</head>
7<body>
8 <h1>You are offline</h1>
9 <p>This is the offline fallback page.</p>
10</body>
11</html>
style.css
1body {
2 font-family: sans-serif;
3 background-color: #f0f0f0;
4 padding: 20px;
5}
6h1 {
7 color: #333;
8}
javascript-service-worker.html
1<!DOCTYPE html>
2<html lang="en">
3<head>
4 <meta charset="UTF-8">
5 <title>JavaScript & HTML</title>
6 <style>
7 * {
8 box-sizing: border-box;
9 }
10
11 body {
12 margin: 0;
13 padding: 1em;
14 padding-bottom: 10em;
15 font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
16 background-color: #f7f9fc;
17 color: #333;
18 line-height: 1.6;
19 }
20
21 .container {
22 max-width: 800px;
23 margin: 0 auto;
24 padding: 1em;
25 background-color: #ffffff;
26 border: 1px solid #ccc;
27 border-radius: 10px;
28 box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
29 }
30
31 .container-flex {
32 display: flex;
33 flex-wrap: wrap;
34 gap: 2em;
35 max-width: 1000px;
36 margin: 0 auto;
37 padding: 1em;
38 background-color: #ffffff;
39 border: 1px solid #ccc;
40 border-radius: 10px;
41 box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
42 }
43
44 .left-column, .right-column {
45 flex: 1 1 200px;
46 min-width: 200px;
47 }
48
49 h1, h2 {
50 font-size: 1.2rem;
51 color: #007bff;
52 margin-top: 0.5em;
53 margin-bottom: 0.5em;
54 border-left: 5px solid #007bff;
55 padding-left: 0.6em;
56 background-color: #e9f2ff;
57 }
58
59 button {
60 display: block;
61 margin: 1em auto;
62 padding: 0.75em 1.5em;
63 font-size: 1rem;
64 background-color: #007bff;
65 color: white;
66 border: none;
67 border-radius: 6px;
68 cursor: pointer;
69 transition: background-color 0.3s ease;
70 }
71
72 button:hover {
73 background-color: #0056b3;
74 }
75
76 #output {
77 margin-top: 1em;
78 background-color: #1e1e1e;
79 color: #0f0;
80 padding: 1em;
81 border-radius: 8px;
82 min-height: 200px;
83 font-family: Consolas, monospace;
84 font-size: 0.95rem;
85 overflow-y: auto;
86 white-space: pre-wrap;
87 }
88
89 .highlight {
90 outline: 3px solid #ffc107; /* yellow border */
91 background-color: #fff8e1; /* soft yellow background */
92 transition: background-color 0.3s ease, outline 0.3s ease;
93 }
94
95 .active {
96 background-color: #28a745; /* green background */
97 color: #fff;
98 box-shadow: 0 0 10px rgba(40, 167, 69, 0.5);
99 transition: background-color 0.3s ease, box-shadow 0.3s ease;
100 }
101 </style>
102</head>
103<body>
104 <div class="container">
105 <h1>JavaScript Console</h1>
106 <button id="executeBtn">Execute</button>
107 <div id="output"></div>
108 </div>
109
110 <div class="container">
111 <h2>HTML Sample</h2>
112 <button id="fetchBtn">Fetch Test</button>
113 </div>
114
115 <script>
116 // Override console.log to display messages in the #output element
117 (function () {
118 // Override console.log
119 const originalLog = console.log;
120 console.log = function (...args) {
121 originalLog.apply(console, args);
122 const message = document.createElement('div');
123 message.textContent = args.map(String).join(' ');
124 output.appendChild(message);
125 };
126
127 // Override console.error
128 const originalError = console.error;
129 console.error = function (...args) {
130 originalError.apply(console, args);
131 const message = document.createElement('div');
132 message.textContent = args.map(String).join(' ');
133 message.style.color = 'red'; // Color error messages red
134 output.appendChild(message);
135 };
136 })();
137
138 document.getElementById('executeBtn').addEventListener('click', () => {
139 // Prevent multiple loads
140 if (document.getElementById('externalScript')) return;
141
142 const script = document.createElement('script');
143 script.src = 'javascript-service-worker.js';
144 script.id = 'externalScript';
145 //script.onload = () => console.log('javascript-service-worker.js loaded and executed.');
146 //script.onerror = () => console.log('Failed to load javascript-service-worker.js.');
147 document.body.appendChild(script);
148 });
149 </script>
150</body>
151</html>
Service Worker
in JavaScript
Service Worker
è una funzionalità JavaScript che si colloca tra il browser e la rete, consentendo la memorizzazione nella cache delle richieste e il supporto offline. È una tecnologia fondamentale per le PWA (Progressive Web Apps) e offre alle applicazioni web un'esperienza simile a quella di un'app nativa.
Cos’è un Service Worker
?
Service Worker
è un file JavaScript che viene eseguito nel thread di background del browser. Viene eseguito su un thread separato rispetto alla pagina, non può accedere all'interfaccia utente, ma può intercettare le richieste di rete, gestire la cache e gestire le notifiche push.
Le caratteristiche principali di un Service Worker
includono:.
- Funziona solo tramite HTTPS, eccetto su localhost.
- Utilizza un'API asincrona basata su Promise.
- È guidato dagli eventi, utilizzando eventi come
install
,activate
,fetch
epush
.
Registrazione di un Service Worker
Per prima cosa, scriviamo il codice per registrare un Service Worker
nel browser.
1if ('serviceWorker' in navigator) {
2 window.addEventListener('load', () => {
3 navigator.serviceWorker.register('/sw.js')
4 .then(registration => {
5 console.log(
6 'Service Worker registered with scope:',
7 registration.scope
8 );
9 })
10 .catch(error => {
11 console.error('Service Worker registration failed:', error);
12 });
13 });
14}
Spiegazione
- Usa
navigator.serviceWorker.register()
per registrare/sw.js
(il fileService Worker
). - Puoi usare
then
per gestire il successo ecatch
per la gestione degli errori durante la registrazione. registration.scope
rappresenta l'intervallo di percorso (scope) interessato dalService Worker
.- Per impostazione predefinita, lo scope è la directory in cui si trova il file registrato (in questo caso,
/sw.js
) e le sue sottodirectory.
Ambito del Service Worker
Se vuoi limitare l'ambito, puoi specificare lo scope
utilizzando il secondo argomento di register
.
1navigator.serviceWorker.register('/sw.js', { scope: '/app/' })
2.then(registration => {
3 console.log(
4 'Service Worker registered with scope:',
5 registration.scope
6 );
7});
Spiegazione
- Con questa impostazione, solo le pagine sotto
/app/
saranno controllate dalService Worker
.
Creazione del file Service Worker
Successivamente, crea un file chiamato sw.js
e implementa gli eventi di base.
1// sw.js
2const CACHE_NAME = 'my-cache-v1';
3const urlsToCache = [
4 '/',
5 '/index.html',
6 '/styles.css',
7 '/script.js',
8 '/offline.html'
9];
Questo codice definisce un elenco di risorse da memorizzare nella cache.
Ruoli e meccanismi di ciascun evento
install
1// Install event (initial caching)
2self.addEventListener('install', event => {
3 console.log('[ServiceWorker] Install');
4 event.waitUntil(
5 caches.open(CACHE_NAME).then(cache => {
6 console.log('[ServiceWorker] Caching app shell');
7 return cache.addAll(urlsToCache);
8 })
9 );
10});
self.addEventListener('install')
viene attivato quando ilService Worker
viene registrato per la prima volta. In questa fase, i file necessari vengono pre-memorizzati nella cache.
activate
1// Activation event (delete old caches)
2self.addEventListener('activate', event => {
3 console.log('[ServiceWorker] Activate');
4 event.waitUntil(
5 caches.keys().then(keyList => {
6 return Promise.all(keyList.map(key => {
7 if (key !== CACHE_NAME) {
8 console.log('[ServiceWorker] Removing old cache:', key);
9 return caches.delete(key);
10 }
11 }));
12 })
13 );
14 return self.clients.claim();
15});
- Nell'evento
activate
, le vecchie cache vengono eliminate per ottimizzare lo spazio di archiviazione. Viene mantenuta solo la cache della nuova versione.
fetch
1// Fetch event (cache-first strategy)
2self.addEventListener('fetch', event => {
3 console.log('[ServiceWorker] Fetch', event.request.url);
4 event.respondWith(
5 caches.match(event.request).then(response => {
6 return response || fetch(event.request).catch(() => caches.match('/offline.html'));
7 })
8 );
9});
Tutte le richieste HTTP vengono intercettate: se esiste una versione in cache, viene restituita; altrimenti viene recuperata dalla rete. Quando sei offline, viene restituita una pagina alternativa (ad esempio, offline.html
).
Conferma dell'operazione
Verifichiamo effettivamente come funziona il Service Worker
.
1document.getElementById('fetchBtn').addEventListener('click', () => {
2 fetch('/style.css')
3 .then(response => response.text())
4 .then(data => {
5 console.log('Fetched data:', data);
6 })
7 .catch(error => {
8 console.error('Fetch failed:', error);
9 });
10});
- Qui verifichiamo la registrazione del
Service Worker
e il comportamento durante il recupero delle risorse facendo clic sul pulsante di prova.
Esempi di strategie di caching
Le seguenti sono strategie di caching comuni:.
Cache First (Cache prima)
Ecco un esempio di implementazione della strategia Cache First:.
1self.addEventListener('fetch', event => {
2 event.respondWith(
3 caches.match(event.request).then(response => {
4 return response || fetch(event.request);
5 })
6 );
7});
- Questo codice implementa una strategia cache-first, in cui la risorsa richiesta viene restituita dalla cache se disponibile; in caso contrario, viene recuperata dalla rete.
Network First (Rete prima)
Ecco un esempio di implementazione della strategia Network First:.
1self.addEventListener('fetch', event => {
2 event.respondWith(
3 fetch(event.request)
4 .then(response => {
5 return caches.open(CACHE_NAME).then(cache => {
6 cache.put(event.request, response.clone());
7 return response;
8 });
9 })
10 .catch(() => caches.match(event.request))
11 );
12});
- Questo codice implementa una strategia network-first, in cui la risorsa richiesta viene prima recuperata dalla rete e, se questo fallisce, viene recuperata dalla cache.
Metti in cache solo gli stili e JavaScript, accedi alle API in tempo reale
Ecco un esempio di implementazione in cui stili e JavaScript vengono memorizzati nella cache mentre le API vengono accessibili in tempo reale:.
1self.addEventListener('fetch', event => {
2 if (event.request.url.includes('/api/')) {
3 // Fetch API responses in real-time without caching
4 return;
5 }
6
7 // Use cache-first strategy for static files
8 event.respondWith(
9 caches.match(event.request).then(response => {
10 return response || fetch(event.request);
11 })
12 );
13});
- Questo codice accede sempre alle richieste API in tempo reale e applica una strategia cache-first ai file statici come fogli di stile e JavaScript.
Flusso di aggiornamento
Il flusso di aggiornamento di un Service Worker è il seguente:.
- Viene rilevato un nuovo
sw.js
. - Viene attivato l'evento
install
. - Attende che il precedente
Service Worker
diventi inattivo. - Viene attivato l'evento
activate
. - Passa al nuovo Service Worker.
- Viene generato l'evento
controllerchange
.
Rilevamento aggiornamenti
Una volta installato un Service Worker
, quello vecchio continua ad essere utilizzato fino alla visita successiva. Per applicare gli aggiornamenti, è comune utilizzare un codice che rileva gli aggiornamenti e ricarica la pagina.
1navigator.serviceWorker.addEventListener('controllerchange', () => {
2 window.location.reload();
3});
- L'evento
controllerchange
viene generato quando cambia il controller del Service Worker, ovvero il Service Worker che controlla la pagina corrente. - Le pagine già aperte continuano ad utilizzare il Service Worker attuale e il nuovo Service Worker installato non viene applicato immediatamente a queste pagine. Pertanto, viene utilizzata una tecnica in cui l'evento
controllerchange
viene utilizzato per rilevare che un nuovo controller è diventato attivo e la pagina viene ricaricata per applicare immediatamente l'aggiornamento.
Avvertenze e best practice
Quando utilizzi i Service Worker
, tieni presenti i seguenti punti:.
-
HTTPS richiesto A causa di restrizioni di sicurezza, non funziona tramite
http://
a eccezione dilocalhost
. -
Nomi di file con hash Il nome della cache può includere il nome del file, l’URL e le informazioni sulla versione.
-
Comunicazione con i client Utilizza
postMessage
per comunicare tra ilService Worker
e il JavaScript della pagina.
Riepilogo
Service Worker
è una tecnologia essenziale per il supporto offline e il miglioramento delle prestazioni nelle app web. Comprendendo il flusso di base di installazione, attivazione e gestione delle fetch, e implementando strategie di caching appropriate, puoi creare applicazioni web di qualità superiore.
Puoi seguire l'articolo sopra utilizzando Visual Studio Code sul nostro canale YouTube. Controlla anche il nostro canale YouTube.