Durante lo sviluppo di un gioco tramite il framework Javascript Phaser 3 ci si può domanda come poter rendere disponibile il proprio progetto anche durante l’assenza di connessione (si pensi a momenti di down del servizio oppure la modalità aerea del proprio dispositivo mobile).
Di seguito quindi la procedura per sfruttare la potenzialità del Service Worker di Javascript per cachare le risorse durante il primo avvio (con connessione attiva) e rendere così disponibili il gioco anche offline.
In questo articolo
Prima di iniziare
E’ bene indicare che il Service Worker per poter funzionare in remoto (su uno spazio web) ha bisogno del protocollo HTTPS, mentre in locale non è strettamente necessario.
Creazione del Service Worker
Creare un file service-worker.js e inserirlo nella root del progetto (prerogativa) e includere il codice seguente:
const CACHE_NAME = 'phaser3-game-dynamic-cache-v1';
// Gestisce l'installazione del Service Worker (senza pre-caricamento delle risorse)
self.addEventListener('install', (event) => {
event.waitUntil(self.skipWaiting()); // Salta subito allo stato "attivo"
});
// Gestisce le richieste e aggiunge in cache eventuali nuove risorse
self.addEventListener('fetch', (event) => {
event.respondWith(
caches.match(event.request).then((response) => {
return response || fetch(event.request).then((networkResponse) => {
// Aggiungi alla cache solo le risorse che non sono presenti
return caches.open(CACHE_NAME).then((cache) => {
cache.put(event.request, networkResponse.clone());
return networkResponse;
});
});
})
);
});
// Pulizia della cache vecchia in fase di attivazione
self.addEventListener('activate', (event) => {
event.waitUntil(
caches.keys().then((cacheNames) => {
return Promise.all(
cacheNames.map((cacheName) => {
if (cacheName !== CACHE_NAME) {
return caches.delete(cacheName);
}
})
);
})
);
});
Avviare il Service Worker
Nella funzione windows.load, prima di inizializzare l’istanza del proprio gioco di Phaser, inserire il codice seguente che verifica il worker e lo istanzia:
if ('serviceWorker' in navigator) {
console.log('Service Worker supportato!');
navigator.serviceWorker.register('service-worker.js')
.then((registration) => {
console.log('Service Worker registrato con successo: ' + registration);
})
.catch((error) => {
console.log( 'Registrazione del Service Worker fallita: ' + error);
});
} else {
console.log('Service Worker non supportato dal browser.');
}
Gestire il Service Worker
E’ possibile verificare la funzionalità del service worker attraverso gli strumenti per sviluppatore del proprio browser desktop, accedendo alla tab Apllication > Service workers e utilizzando le funzione di Offline e Update On Reload.
Invalidare la cache del Service Worker
Durante lo sviluppo di un applicativo gestito dal Service Worker ci si potrebbe scontrare con l’involontario caricamento delle risorse dalla cache e non da files modificati. Questo significa che il servizio sta ancora leggendo dalla cache e non l’ha refreshata.
Un modo per farlo completamente è quello di usare il versioning del nome della cache.
Nell’esempio presente in questo articolo è stato infatti utilizzata la nomenclatura:
const CACHE_NAME = ‘phaser3-game-dynamic-cache-v1’;
Se necessario è quindi possibile cambiare il numero di versione del suffisso finale della stringa (da v1 a v2, v3, etc) in modo che il Service Worker capisca che è stato modificato e dunque reinizializzarà la cache nuovamente.
ALTERNATIVA: Invalidare singolarmente i files
Una alternativa, se si preferisce non cambiare CACHE_NAME
è quella di aggiungere aggiungere dei “cache-busting” URL delle risorse caricate. Ad esempio, invece di /main.js
, si può aggiornare il file a /main.js?v=2
ogni volta che se ne fa’ una modifica significativa. Questo metodo forza il browser a ricaricare l’asset in quanto riconosce l’URL come differente.
Verificare lo spazio occupato dalla cache del Service Worker
Per verificare quanto spazio è disponibile nel proprio browser web (può variare, anche di molto, tra i diversi browser desktop e mobile) utilizzare la funzione seguente:
if (navigator.storage && navigator.storage.estimate) {
navigator.storage.estimate().then(({ quota, usage }) => {
const quotaMB = (quota / (1024 * 1024)).toFixed(2); // Converti da byte a MB
const usageMB = (usage / (1024 * 1024)).toFixed(2);
const remainingMB = (quotaMB - usageMB).toFixed(2);
console.log(`Spazio totale disponibile: ${quotaMB} MB`);
console.log(`Spazio utilizzato: ${usageMB} MB`);
console.log(`Spazio rimanente: ${remainingMB} MB`);
});
}