Memorizzazione delle risorse nella cache durante il runtime

Alcuni asset nella tua applicazione web potrebbero essere utilizzati raramente, essere molto grandi o variare in base al dispositivo dell'utente (ad esempio, le immagini adattabili) o alla lingua. Si tratta di istanze in cui la pre-memorizzazione nella cache può costituire un anti-pattern ed è invece necessario ricorrere alla memorizzazione nella cache di runtime.

In Workbox, puoi gestire la memorizzazione nella cache di runtime degli asset utilizzando il modulo workbox-routing per abbinare le route e le strategie di memorizzazione nella cache con il modulo workbox-strategies.

Strategie di memorizzazione nella cache

Puoi gestire la maggior parte delle route per gli asset con una delle strategie di memorizzazione nella cache integrate. Sono stati trattati in dettaglio in precedenza in questa documentazione, ma ecco alcuni esempi che vale la pena ricapitolare:

  • L'opzione Inattiva durante la riconvalida utilizza una risposta memorizzata nella cache per una richiesta, se disponibile, e aggiorna la cache in background con una risposta della rete. Pertanto, se l'asset non viene memorizzato nella cache, attenderà la risposta della rete e la utilizzerà. È una strategia abbastanza sicura, in quanto aggiorna regolarmente le voci della cache che si basano su di essa. Lo svantaggio è che richiede sempre una risorsa alla rete in background.
  • Network First tenta prima di ricevere una risposta dalla rete. Quando la risposta viene ricevuta, la passa al browser e la salva in una cache. Se la richiesta di rete non va a buon fine, verrà utilizzata l'ultima risposta memorizzata nella cache, che consente l'accesso offline all'asset.
  • Prima cache controlla prima la presenza di una risposta nella cache e la utilizza, se disponibile. Se la richiesta non è presente nella cache, viene utilizzata la rete e qualsiasi risposta valida viene aggiunta alla cache prima di essere passata al browser.
  • Solo rete obbliga la risposta a provenire dalla rete.
  • Solo cache impone che la risposta provenga dalla cache.

Puoi applicare queste strategie per selezionare le richieste utilizzando i metodi offerti da workbox-routing.

Applicazione di strategie di memorizzazione nella cache con la corrispondenza delle route

workbox-routing espone un metodo registerRoute per abbinare le route e gestirle con una strategia di memorizzazione nella cache. registerRoute accetta un oggetto Route che a sua volta accetta due argomenti:

  1. Una stringa, un'espressione regolare o un callback di corrispondenza per specificare i criteri di corrispondenza della route.
  2. Un gestore per la route, in genere una strategia fornita da workbox-strategies.

È preferibile usare i callback di corrispondenza per trovare corrispondenze con le route, in quanto forniscono un oggetto di contesto che include l'oggetto Request, la stringa dell'URL della richiesta, l'evento di recupero e un valore booleano che indica se la richiesta è o meno una richiesta della stessa origine.

Il gestore gestisce quindi la route corrispondente. Nell'esempio seguente, viene creata una nuova route che corrisponde alle richieste di immagini della stessa origine in arrivo, applicando prima la cache, attingendo alla strategia di rete.

// sw.js
import { registerRoute, Route } from 'workbox-routing';
import { CacheFirst } from 'workbox-strategies';

// A new route that matches same-origin image requests and handles
// them with the cache-first, falling back to network strategy:
const imageRoute = new Route(({ request, sameOrigin }) => {
  return sameOrigin && request.destination === 'image'
}, new CacheFirst());

// Register the new route
registerRoute(imageRoute);

Utilizzo di più cache

Workbox ti consente di raggruppare le risposte memorizzate nella cache in istanze Cache separate utilizzando l'opzione cacheName disponibile nelle strategie di bundle.

Nell'esempio seguente, le immagini utilizzano una strategia inattiva durante la riconvalida, mentre gli asset CSS e JavaScript utilizzano una strategia di riserva basata sulla cache. Il percorso di ogni asset inserisce le risposte in cache separate, aggiungendo la proprietà cacheName.

// sw.js
import { registerRoute, Route } from 'workbox-routing';
import { CacheFirst, StaleWhileRevalidate } from 'workbox-strategies';

// Handle images:
const imageRoute = new Route(({ request }) => {
  return request.destination === 'image'
}, new StaleWhileRevalidate({
  cacheName: 'images'
}));

// Handle scripts:
const scriptsRoute = new Route(({ request }) => {
  return request.destination === 'script';
}, new CacheFirst({
  cacheName: 'scripts'
}));

// Handle styles:
const stylesRoute = new Route(({ request }) => {
  return request.destination === 'style';
}, new CacheFirst({
  cacheName: 'styles'
}));

// Register routes
registerRoute(imageRoute);
registerRoute(scriptsRoute);
registerRoute(stylesRoute);
Uno screenshot di un elenco di istanze cache nella scheda dell'applicazione di DevTools di Chrome. Sono visualizzate tre cache distinte: una denominata "scripts", un'altra denominata "styles" e l'ultima denominata "images".
Visualizzatore dello spazio di archiviazione della cache nel riquadro Applicazione di Chrome DevTools. Le risposte per diversi tipi di asset vengono archiviate in cache separate.

Impostare una scadenza per le voci della cache

Tieni presente le quote di archiviazione quando gestisci le cache dei service worker. ExpirationPlugin semplifica la manutenzione della cache ed è esposto da workbox-expiration. Per utilizzarlo, specificalo nella configurazione di una strategia di memorizzazione nella cache:

// sw.js
import { registerRoute, Route } from 'workbox-routing';
import { CacheFirst } from 'workbox-strategies';
import { ExpirationPlugin } from 'workbox-expiration';

// Evict image cache entries older thirty days:
const imageRoute = new Route(({ request }) => {
  return request.destination === 'image';
}, new CacheFirst({
  cacheName: 'images',
  plugins: [
    new ExpirationPlugin({
      maxAgeSeconds: 60 * 60 * 24 * 30,
    })
  ]
}));

// Evict the least-used script cache entries when
// the cache has more than 50 entries:
const scriptsRoute = new Route(({ request }) => {
  return request.destination === 'script';
}, new CacheFirst({
  cacheName: 'scripts',
  plugins: [
    new ExpirationPlugin({
      maxEntries: 50,
    })
  ]
}));

// Register routes
registerRoute(imageRoute);
registerRoute(scriptsRoute);
configurato

Il rispetto delle quote di spazio di archiviazione può essere complicato. È buona norma prendere in considerazione gli utenti che potrebbero trovarsi in difficoltà nello spazio di archiviazione o che vogliono utilizzare al meglio il proprio spazio di archiviazione. Le coppie di ExpirationPlugin di Workbox possono aiutarti a raggiungere l'obiettivo.

Considerazioni multiorigine

L'interazione tra il service worker e gli asset multiorigine è notevolmente diversa da quella con gli asset della stessa origine. La condivisione delle risorse tra origini (CORS) è complicata e si estende al modo in cui gestisci le risorse multiorigine in un service worker.

Risposte opache

Quando si effettua una richiesta multiorigine in modalità no-cors, la risposta può essere archiviata in una cache del service worker e persino utilizzata direttamente dal browser. Tuttavia, il corpo della risposta stesso non può essere letto tramite JavaScript. Questo è noto come risposta opaca.

Le risposte poco chiare sono una misura di sicurezza destinata a impedire l'ispezione di una risorsa multiorigine. Puoi comunque effettuare richieste per asset multiorigine e persino memorizzarli nella cache, ma non puoi leggere il corpo della risposta o persino leggere il relativo codice di stato.

Ricordati di attivare la modalità CORS

Anche se carichi asset multiorigine che impostano intestazioni CORS permissive che consentono di leggere le risposte, il corpo della risposta multiorigine potrebbe essere ancora opaco. Ad esempio, il seguente codice HTML attiverà richieste no-cors che porteranno a risposte opache indipendentemente dalle intestazioni CORS impostate:

<link rel="stylesheet" href="https://example.com/path/to/style.css">
<img src="https://example.com/path/to/image.png">

Per attivare esplicitamente una richiesta cors che produrrà una risposta non opaca, devi attivare esplicitamente la modalità CORS aggiungendo l'attributo crossorigin al codice HTML:

<link crossorigin="anonymous" rel="stylesheet" href="https://example.com/path/to/style.css">
<img crossorigin="anonymous" src="https://example.com/path/to/image.png">

È importante ricordare quando le route nelle risorse secondarie della cache del service worker vengono caricate durante il runtime.

La casella di lavoro non può memorizzare nella cache risposte opache

Per impostazione predefinita, Workbox adotta un approccio cautelativo alla memorizzazione nella cache di risposte opache. Poiché è impossibile esaminare il codice di risposta per risposte opache, la memorizzazione nella cache di una risposta di errore può comportare un'interruzione permanente se si utilizza una strategia basata sulla cache o solo cache.

Se devi memorizzare nella cache una risposta opaca in Workbox, devi utilizzare una strategia Network-first o In attesa di Convalida (Inattività) per gestirla. Sì, ciò significa che l'asset verrà comunque richiesto dalla rete ogni volta, ma garantisce che le risposte non riuscite non vengano mantenute e alla fine verranno sostituite da risposte utilizzabili.

Se utilizzi un'altra strategia di memorizzazione nella cache e viene restituita una risposta opaca, Workbox ti avvisa che la risposta non è stata memorizzata nella cache in modalità sviluppo.

Forza la memorizzazione nella cache di risposte opache

Se sei assolutamente sicuro di voler memorizzare nella cache una risposta opaca utilizzando una strategia di tipo cache-first o solo cache, puoi forzare Workbox a farlo con il modulo workbox-cacheable-response:

import {Route, registerRoute} from 'workbox-routing';
import {NetworkFirst, StaleWhileRevalidate} from 'workbox-strategies';
import {CacheableResponsePlugin} from 'workbox-cacheable-response';

const cdnRoute = new Route(({url}) => {
  return url === 'https://cdn.google.com/example-script.min.js';
}, new CacheFirst({
  plugins: [
    new CacheableResponsePlugin({
      statuses: [0, 200]
    })
  ]
}))

registerRoute(cdnRoute);

Opaque Responses e l'API navigator.storage

Per evitare la perdita di informazioni tra domini, viene aggiunta una spaziatura interna significativa alle dimensioni di una risposta opaca utilizzata per calcolare i limiti della quota di spazio di archiviazione. Questo influisce sul modo in cui l'API navigator.storage segnala le quote di archiviazione.

La spaziatura interna varia a seconda del browser, ma per Chrome la dimensione minima che ogni singola risposta opaca memorizzata nella cache contribuisce allo spazio di archiviazione complessivo utilizzato è di circa 7 MB. Devi tenere presente questo aspetto al momento di determinare quante risposte opache vuoi memorizzare nella cache, dato che potresti facilmente superare le quote di archiviazione molto prima di quanto ti aspetteresti.