Strategie per la memorizzazione nella cache dei service worker

Finora, ci sono stati solo menzioni e piccoli snippet di codice dei l'interfaccia di Cache. Per utilizzare i service worker in modo efficace, è necessario adottare una o più strategie di memorizzazione nella cache, il che richiede una certa familiarità con l'interfaccia Cache.

Una strategia di memorizzazione nella cache è un'interazione tra l'evento fetch di un service worker e l'interfaccia Cache. Il modo in cui viene scritta una strategia di memorizzazione nella cache dipende: Ad esempio, potrebbe essere preferibile gestire le richieste per le risorse statiche in modo diverso rispetto ai documenti, e questo influisce sul modo in cui è composta la strategia di memorizzazione nella cache.

Prima di passare alle strategie, soffermiamoci un attimo a parlare di cosa non è l'interfaccia di Cache, che cos'è, e un rapido resoconto di alcuni dei metodi che offre per gestire le cache dei service worker.

Interfaccia Cache e cache HTTP

Se non hai mai lavorato con l'interfaccia Cache, potresti avere la tentazione di considerarlo uguale o almeno relativi alla cache HTTP. Questo non è il caso.

  • L'interfaccia Cache è un meccanismo di memorizzazione nella cache completamente separato dalla cache HTTP.
  • Qualsiasi cosa Cache-Control configurazione che utilizzi per influenzare la cache HTTP non influisce sugli asset archiviati nell'interfaccia di Cache.

È utile considerare le cache del browser come stratificate. La cache HTTP è una cache di basso livello gestita da coppie chiave-valore con istruzioni espresse nelle intestazioni HTTP.

Al contrario, l'interfaccia Cache è una cache di alto livello basata su un'API JavaScript. Questo offre maggiore flessibilità rispetto all'utilizzo di coppie chiave-valore HTTP relativamente semplici, ed è la metà di ciò che rende possibili le strategie di memorizzazione nella cache. Alcuni importanti metodi dell'API per le cache dei service worker sono:

  • CacheStorage.open per creare una nuova istanza Cache.
  • Cache.add e Cache.put per archiviare le risposte di rete nella cache dei service worker.
  • Cache.match per individuare una risposta memorizzata nella cache in un'istanza Cache.
  • Cache.delete per rimuovere una risposta memorizzata nella cache da un'istanza Cache.

solo per citarne alcuni. Esistono altri metodi utili, ma queste sono quelle di base che vedrai più avanti in questa guida.

L'umile evento fetch

L'altra metà della strategia di memorizzazione nella cache è Evento fetch. Finora in questa documentazione hai sentito parlare di "intercettazione delle richieste di rete", e si verifica l'evento fetch all'interno di un service worker:

// Establish a cache name
const cacheName = 'MyFancyCacheName_v1';

self.addEventListener('install', (event) => {
  event.waitUntil(caches.open(cacheName));
});

self.addEventListener('fetch', async (event) => {
  // Is this a request for an image?
  if (event.request.destination === 'image') {
    // Open the cache
    event.respondWith(caches.open(cacheName).then((cache) => {
      // Respond with the image from the cache or from the network
      return cache.match(event.request).then((cachedResponse) => {
        return cachedResponse || fetch(event.request.url).then((fetchedResponse) => {
          // Add the network response to the cache for future visits.
          // Note: we need to make a copy of the response to save it in
          // the cache and use the original as the request response.
          cache.put(event.request, fetchedResponse.clone());

          // Return the network response
          return fetchedResponse;
        });
      });
    }));
  } else {
    return;
  }
});

Questo è un esempio di giocattolo che puoi vedere in azione, offre un'idea di cosa possono fare i Service worker. Il codice riportato sopra fa quanto segue:

  1. Controlla la proprietà destination della richiesta per verificare se si tratta di una richiesta di immagine.
  2. Se l'immagine si trova nella cache del service worker, pubblicala da lì. In caso contrario, recupera l'immagine dalla rete archiviare la risposta nella cache e restituire la risposta di rete.
  3. Tutte le altre richieste vengono passate attraverso il service worker senza interagire con la cache.

L'oggetto event di un recupero contiene un Proprietà request che forniscono alcune informazioni utili per aiutarti a identificare il tipo di ogni richiesta:

  • url, che è l'URL della richiesta di rete attualmente gestita dall'evento fetch.
  • method, che è il metodo di richiesta (ad es. GET o POST).
  • mode, che descrive la modalità della richiesta. Spesso viene utilizzato un valore 'navigate' per distinguere le richieste di documenti HTML da altre richieste.
  • destination, che descrive il tipo di contenuti richiesti in modo da evitare di utilizzare l'estensione del file dell'asset richiesto.

Ancora una volta, il nome del gioco è asincrono. Ricorderai che l'evento install offre un event.waitUntil che accetta una promessa e attende che venga risolta prima di procedere con l'attivazione. L'evento fetch offre una modalità simile Metodo event.respondWith che puoi utilizzare per restituire il risultato di un Richiesta di fetch o una risposta restituita dall'interfaccia Cache match.

Strategie di memorizzazione nella cache

Ora che hai acquisito familiarità con le istanze Cache e il gestore di eventi fetch, puoi scoprire alcune strategie di memorizzazione nella cache dei service worker. Sebbene le possibilità siano praticamente infinite, questa guida illustrerà le strategie incluse in Workbox, per farti un'idea di cosa succede all'interno di Workbox.

Solo cache

Mostra il flusso dalla pagina al service worker, fino alla cache.

Iniziamo con una semplice strategia di memorizzazione nella cache che chiameremo "Solo cache". Semplicemente, quando il service worker controlla la pagina, le richieste con corrispondenza verranno trasferite solo nella cache. Ciò significa che gli asset memorizzati nella cache dovranno essere prememorizzati nella cache per renderli disponibili affinché il pattern funzioni. e che questi asset non vengano mai aggiornati nella cache fino all'aggiornamento del service worker.

// Establish a cache name
const cacheName = 'MyFancyCacheName_v1';

// Assets to precache
const precachedAssets = [
  '/possum1.jpg',
  '/possum2.jpg',
  '/possum3.jpg',
  '/possum4.jpg'
];

self.addEventListener('install', (event) => {
  // Precache assets on install
  event.waitUntil(caches.open(cacheName).then((cache) => {
    return cache.addAll(precachedAssets);
  }));
});

self.addEventListener('fetch', (event) => {
  // Is this one of our precached assets?
  const url = new URL(event.request.url);
  const isPrecachedRequest = precachedAssets.includes(url.pathname);

  if (isPrecachedRequest) {
    // Grab the precached asset from the cache
    event.respondWith(caches.open(cacheName).then((cache) => {
      return cache.match(event.request.url);
    }));
  } else {
    // Go to the network
    return;
  }
});

Sopra, un array di asset viene prememorizzato al momento dell'installazione. Quando il service worker gestisce i recuperi, verifichiamo se l'URL della richiesta gestito dall'evento fetch si trova nell'array di asset prememorizzati nella cache. In questo caso, prendiamo la risorsa dalla cache e ignoriamo la rete. Altre richieste passano attraverso la rete, e solo la rete. Per vedere la strategia in azione, guarda questa demo con la console aperta.

Solo rete

Mostra il flusso dalla pagina al service worker, alla rete.

Il contrario di "Solo cache" è "Solo rete", in cui una richiesta viene fatta passare attraverso un service worker alla rete senza alcuna interazione con la cache del service worker. Si tratta di una buona strategia per garantire l'aggiornamento dei contenuti, ad esempio il markup. ma c'è un compromesso: non funzionerà mai quando l'utente è offline.

Se una richiesta passa attraverso la rete, significa semplicemente non chiamare event.respondWith per una richiesta con corrispondenza. Se vuoi essere esplicito, puoi schiacciare uno return; vuoto nel callback dell'evento fetch per le richieste che vuoi passare alla rete. Ecco cosa succede nella sezione "Solo cache" per le richieste che non sono prememorizzate nella cache.

Prima memorizza nella cache, poi torna alla rete

Mostra il flusso dalla pagina al service worker, alla cache e infine alla rete se non si trova nella cache.

È in questa strategia che le cose diventano un po' più coinvolte. Per le richieste con corrispondenza, la procedura è la seguente:

  1. La richiesta raggiunge la cache. Se l'asset si trova nella cache, pubblicalo da lì.
  2. Se la richiesta non si trova nella cache, accedi alla rete.
  3. Al termine della richiesta di rete, aggiungila alla cache per poi restituire la risposta dalla rete.

Ecco un esempio di questa strategia, che puoi testare in una demo dal vivo:

// Establish a cache name
const cacheName = 'MyFancyCacheName_v1';

self.addEventListener('fetch', (event) => {
  // Check if this is a request for an image
  if (event.request.destination === 'image') {
    event.respondWith(caches.open(cacheName).then((cache) => {
      // Go to the cache first
      return cache.match(event.request.url).then((cachedResponse) => {
        // Return a cached response if we have one
        if (cachedResponse) {
          return cachedResponse;
        }

        // Otherwise, hit the network
        return fetch(event.request).then((fetchedResponse) => {
          // Add the network response to the cache for later visits
          cache.put(event.request, fetchedResponse.clone());

          // Return the network response
          return fetchedResponse;
        });
      });
    }));
  } else {
    return;
  }
});

Anche se questo esempio riguarda solo le immagini, questa è un'ottima strategia da applicare a tutti gli asset statici (come CSS, JavaScript, immagini e caratteri), in particolare quelli con versione hash. Offre un aumento di velocità per le risorse immutabili, aggirando eventuali controlli di aggiornamento dei contenuti con il server su cui la cache HTTP potrebbe essere avviata. Ma soprattutto, tutti gli asset memorizzati nella cache saranno disponibili offline.

Prima sulla rete, poi tornando alla cache

Mostra il flusso dalla pagina al service worker, alla rete, quindi alla cache se la rete non è disponibile.

Se capoverti "Prima cache, poi rete" in testa, si verifica il messaggio "Prima la rete, poi la cache" strategia, che è il suo significato:

  1. Devi prima andare alla rete per richiedere una richiesta e inserire la risposta nella cache.
  2. Se in seguito sarai offline, dovrai scaricare la versione più recente della risposta nella cache.

Questa strategia è ideale per le richieste HTML o API quando mentre sei online, vuoi la versione più recente di una risorsa, vogliono concedere l'accesso offline alla versione più recente disponibile. Ecco come potrebbe apparire quando viene applicato a richieste di HTML:

// Establish a cache name
const cacheName = 'MyFancyCacheName_v1';

self.addEventListener('fetch', (event) => {
  // Check if this is a navigation request
  if (event.request.mode === 'navigate') {
    // Open the cache
    event.respondWith(caches.open(cacheName).then((cache) => {
      // Go to the network first
      return fetch(event.request.url).then((fetchedResponse) => {
        cache.put(event.request, fetchedResponse.clone());

        return fetchedResponse;
      }).catch(() => {
        // If the network is unavailable, get
        return cache.match(event.request.url);
      });
    }));
  } else {
    return;
  }
});

Puoi provare questa funzionalità in una demo. Innanzitutto, vai alla pagina. Potrebbe essere necessario ricaricare prima che la risposta HTML venga inserita nella cache. Poi, negli strumenti per sviluppatori, simulare una connessione offline, e ricaricarla. L'ultima versione disponibile verrà pubblicata immediatamente dalla cache.

Nei casi in cui la funzionalità offline è importante, ma è necessario bilanciare questa funzionalità con l'accesso alla versione più recente di un po' di markup o di dati delle API, "Prima sulla rete, poi sulla cache" è una strategia efficace per raggiungere questo obiettivo.

Inattiva-durante la riconvalida

Mostra il flusso dalla pagina al service worker, alla cache e quindi dalla rete alla cache.

Tra le strategie che abbiamo trattato finora, "Inibito durante la riconvalida" è la più complessa. Per certi versi è simile alle ultime due strategie, ma la procedura dà priorità alla velocità di accesso a una risorsa, e allo stesso tempo aggiorna il video in background. Questa strategia è simile a:

  1. Durante la prima richiesta di un asset, recuperalo dalla rete, nella cache e restituire la risposta di rete.
  2. Nelle richieste successive, pubblica prima l'asset dalla cache e poi "in background". richiedila nuovamente alla rete e aggiorna la voce della cache dell'asset.
  3. Per le richieste successive, riceverai l'ultima versione recuperata dalla rete che era stata inserita nella cache nel passaggio precedente.

Si tratta di una strategia eccellente per ciò che è un po' importante da tenere aggiornati, ma non sono cruciali. Pensa agli elementi come agli avatar per un sito di social media. Vengono aggiornate quando gli utenti lo fanno, ma la versione più recente non è strettamente necessaria per ogni richiesta.

// Establish a cache name
const cacheName = 'MyFancyCacheName_v1';

self.addEventListener('fetch', (event) => {
  if (event.request.destination === 'image') {
    event.respondWith(caches.open(cacheName).then((cache) => {
      return cache.match(event.request).then((cachedResponse) => {
        const fetchedResponse = fetch(event.request).then((networkResponse) => {
          cache.put(event.request, networkResponse.clone());

          return networkResponse;
        });

        return cachedResponse || fetchedResponse;
      });
    }));
  } else {
    return;
  }
});

Puoi vedere come funziona un'altra demo dal vivo, in particolare se fai attenzione alla scheda Rete negli strumenti per sviluppatori del browser, e il relativo visualizzatore CacheStorage (se gli strumenti per sviluppatori del tuo browser dispongono di questo strumento).

Avanti a Workbox!

Questo documento racchiude la nostra revisione dell'API del service worker, nonché le API correlate, il che significa che hai imparato abbastanza su come utilizzare direttamente i service worker per iniziare ad armeggiare con Workbox.