Guide sur la mise en cache impérative

Andrew Guan
Andrew Guan
Demián Renzulli
Demián Renzulli

Certains sites Web peuvent avoir besoin de communiquer avec le service worker sans avoir à être informé du résultat. Voici quelques exemples :

  • Une page envoie au service worker une liste d'URL préchargement, de sorte que, lorsque l'utilisateur clique sur lier les sous-ressources du document ou de la page sont déjà disponibles dans le cache, ce qui permet aux beaucoup plus rapidement la navigation.
  • La page demande au service worker de récupérer et de mettre en cache un ensemble d'articles populaires pour les récupérer disponibles hors connexion.

Déléguer ces types de tâches non critiques au service worker permet de libérer thread principal pour mieux gérer les tâches urgentes, comme répondre aux interactions des utilisateurs.

Schéma d'une page demandant des ressources à mettre en cache à un service worker.

Dans ce guide, nous allons voir comment mettre en œuvre une technique de communication à sens unique, de la page à le service worker à l'aide d'API de navigateur standards et de la bibliothèque Workbox. Nous appellerons ces types de dans certains cas d'utilisation : mise en cache impérative.

Cas de production

1-800-Flowers.com a mis en œuvre la mise en cache impérative (préchargement) avec des service workers via postMessage() pour précharger principaux des pages de catégorie afin d'accélérer la navigation vers les pages d'informations détaillées sur les produits

Logo de 1-800 Flowers.

Elles utilisent une approche mixte pour déterminer les éléments à précharger:

  • Au moment du chargement de la page, il demande au servicer de récupérer les données JSON des 9 principaux éléments. ajouter les objets de réponse obtenus au cache.
  • Pour les autres éléments, il écoute l'mouseover . Ainsi, lorsqu'un l'utilisateur place le curseur au-dessus d'un élément, il peut déclencher une extraction de la ressource à la "demande".

Ils utilisent l'API Cache pour stocker les données JSON. réponses:

<ph type="x-smartling-placeholder">
</ph> Logo de 1-800 Flowers.
Préchargement des données produit JSON des pages de fiches produit du site 1-800Flowers.com
.

Lorsque l'utilisateur clique sur un élément, les données JSON qui y sont associées peuvent être récupérées dans le cache, sans passer par le réseau, ce qui accélère la navigation.

Utiliser Workbox

Workbox permet d'envoyer facilement des messages à un service worker, via le package workbox-window, un ensemble de modules destinées à s'exécuter dans le contexte d'une fenêtre. Ils viennent compléter les autres packages Workbox. qui s'exécutent sur le service worker.

Pour communiquer la page avec le service worker, obtenez d'abord une référence d'objet Workbox à la classe service worker enregistré:

const wb = new Workbox('/sw.js');
wb.register();

Vous pouvez ensuite envoyer le message directement de manière déclarative, sans avoir à récupérer l'inscription, la vérification de l'activation ou la réflexion sur l'API de communication sous-jacente:

wb.messageSW({"type": "PREFETCH", "payload": {"urls": ["/data1.json", "data2.json"]}}); });

Le service worker implémente un gestionnaire message pour écouter ces messages. Elle peut éventuellement renvoyer une réponse, bien que dans de tels cas, non nécessaires:

self.addEventListener('message', (event) => {
  if (event.data && event.data.type === 'PREFETCH') {
    // do something
  }
});

Utiliser les API du navigateur

Si la bibliothèque Workbox ne répond pas à vos besoins, voici comment implémenter une fenêtre de service à l'aide d'API de navigateur.

L'API postMessage permet d'établir un mécanisme de communication à sens unique entre la page et le service worker.

La page appelle postMessage() sur le service worker:

navigator.serviceWorker.controller.postMessage({
  type: 'MSG_ID',
  payload: 'some data to perform the task',
});

Le service worker implémente un gestionnaire message pour écouter ces messages.

self.addEventListener('message', (event) => {
  if (event.data && event.data.type === MSG_ID) {
    // do something
  }
});

L'attribut {type : 'MSG_ID'} n'est pas absolument obligatoire, mais il permet à la page de envoyer différents types d'instructions au service worker (par exemple, "to prefetch" (précharger) et "to clear" (pour effacer) ; stockage"). Le service worker peut comporter des branches différentes en fonction de cette option.

Si l'opération a réussi, l'utilisateur pourra en bénéficier, mais si ce n'est pas le cas, cela ne modifiera pas le flux utilisateur principal. Par exemple, lorsque 1-800-Flowers.com tente d'effectuer une mise en pré-cache, la page n'a pas besoin de savoir si le service worker a réussi. Si c'est le cas, l'utilisateur profitera d'une navigation plus rapide. Si ce n'est pas le cas, la page doit quand même accéder à la nouvelle page. Ça va prendre un peu plus de temps.

Exemple de préchargement simple

Le préchargement est l'une des applications les plus courantes de la mise en cache impérative : pour une URL donnée, avant que l'utilisateur ne s'y rende, afin d'accélérer la navigation.

Il existe différentes manières d'implémenter le préchargement sur les sites:

Pour des scénarios de préchargement relativement simples, tels que le préchargement de documents ou d'éléments spécifiques (JS, CSS, etc.), il s'agit de la meilleure approche.

Si une logique supplémentaire est nécessaire, par exemple, analyser la ressource de préchargement (un fichier ou une page JSON) dans pour récupérer ses URL internes, il est plus approprié de déléguer entièrement cette tâche un service worker.

La délégation de ces types d'opérations au service worker présente les avantages suivants:

  • Se décharger des tâches fastidieuses liées à l'extraction et au traitement post-extraction (qui sera introduite ultérieurement) à un thread secondaire. Cela libère le thread principal pour qu'il gère des tâches tâches telles que la réponse aux interactions des utilisateurs.
  • Permettre à plusieurs clients (des onglets, par exemple) de réutiliser une fonctionnalité commune, et même appeler la méthode service simultanément sans bloquer le thread principal.

Prélire les pages d'informations détaillées sur les produits

Utiliser postMessage() pour la première fois sur l'interface du service worker et transmettre un tableau d'URL à mettre en cache:

navigator.serviceWorker.controller.postMessage({
  type: 'PREFETCH',
  payload: {
    urls: [
      'www.exmaple.com/apis/data_1.json',
      'www.exmaple.com/apis/data_2.json',
    ],
  },
});

Dans le service worker, implémentez un gestionnaire message pour intercepter et traiter les messages envoyés par n'importe quel onglet actif:

addEventListener('message', (event) => {
  let data = event.data;
  if (data && data.type === 'PREFETCH') {
    let urls = data.payload.urls;
    for (let i in urls) {
      fetchAsync(urls[i]);
    }
  }
});

Dans le code précédent, nous avons introduit une petite fonction d'assistance appelée fetchAsync() pour itérer sur le d'URL et envoyez une demande d'extraction pour chacune d'elles:

async function fetchAsync(url) {
  // await response of fetch call
  let prefetched = await fetch(url);
  // (optionally) cache resources in the service worker storage
}

Une fois la réponse obtenue, vous pouvez vous fier aux en-têtes de mise en cache de la ressource. Dans de nombreux cas Toutefois, comme dans les pages d'informations détaillées sur les produits, les ressources ne sont pas mises en cache. en-tête Cache-control de no-cache). Dans de tels cas, vous pouvez contourner ce comportement, en en stockant la ressource récupérée dans le cache du service worker. Cela présente l'avantage supplémentaire d'autoriser à diffuser hors connexion.

Au-delà des données JSON

Une fois que les données JSON sont récupérées à partir d'un point de terminaison de serveur, elles contiennent souvent d'autres URL qui sont également pouvant être préchargées, comme une image ou d'autres données de point de terminaison associées à ce premier niveau données.

Supposons que, dans notre exemple, les données JSON renvoyées correspondent aux informations d'un site de vente en ligne:

{
  "productName": "banana",
  "productPic": "https://cdn.example.com/product_images/banana.jpeg",
  "unitPrice": "1.99"
 }

Modifiez le code fetchAsync() pour itérer la liste des produits et mettre en cache l'image héros pour chacun d'entre eux:

async function fetchAsync(url, postProcess) {
  // await response of fetch call
  let prefetched = await fetch(url);

  //(optionally) cache resource in the service worker cache

  // carry out the post fetch process if supplied
  if (postProcess) {
    await postProcess(prefetched);
  }
}

async function postProcess(prefetched) {
  let productJson = await prefetched.json();
  if (productJson && productJson.product_pic) {
    fetchAsync(productJson.product_pic);
  }
}

Vous pouvez ajouter une gestion des exceptions autour de ce code pour des situations telles que les erreurs 404. Mais l'avantage d'un service worker pour le préchargement, c'est qu'il peut échouer sans trop à la page et au fil de discussion principal. Vous pouvez également appliquer une logique plus élaborée le post-traitement du contenu préchargé, ce qui le rend plus flexible et découplé avec les données gestion. Il n'y a pas de limite.

Conclusion

Dans cet article, nous avons abordé un cas d'utilisation courant de communication à sens unique entre une page et un service. worker: mise en cache impérative. Les exemples présentés ici ne servent qu'à illustrer une façon utilisant ce modèle, et la même approche peut s'appliquer à d'autres cas d'utilisation. Par exemple, mettre en cache les meilleurs articles à la demande pour une consultation hors connexion, l'ajout de favoris, etc.

Pour découvrir d'autres modèles de communication entre les agents de service et les pages Web, consultez les ressources suivantes:

  • Diffusion des mises à jour: appel de la page à partir du service worker pour informer sur les mises à jour importantes (par exemple, une nouvelle version de l'application Web est disponible).
  • Communication bidirectionnelle: déléguer une tâche à un service worker (par exemple, un téléchargement important) et tenir la page informée de la progression.