API Cache: guide rapide

Découvrez comment utiliser l'API Cache pour rendre les données de votre application disponibles hors connexion.

L'API Cache est un système permettant de stocker et de récupérer les requêtes réseau et leurs réponses correspondantes. Il peut s'agir de requêtes et de réponses régulières créées lors de l'exécution de votre application, ou elles peuvent être créées uniquement dans le but de stocker des données pour une utilisation ultérieure.

L'API Cache a été créée pour permettre aux services workers de mettre en cache les requêtes réseau afin de pouvoir fournir des réponses rapides, quelle que soit la vitesse ou la disponibilité du réseau. Toutefois, l'API peut également être utilisée comme mécanisme de stockage général.

L'API Cache est disponible dans tous les navigateurs modernes. Elle est exposée via la propriété caches globale. Vous pouvez donc tester la présence de l'API avec une détection de fonctionnalités simple:

const cacheAvailable = 'caches' in self;

Navigateurs pris en charge

  • Chrome: 40.
  • Edge: 16.
  • Firefox: 41.
  • Safari: 11.1.

Source

Vous pouvez accéder à l'API Cache depuis une fenêtre, une iframe, un nœud de calcul ou un service worker.

Données pouvant être stockées

Les caches ne stockent que des paires d'objets Request et Response, représentant respectivement les requêtes et les réponses HTTP. Toutefois, les requêtes et les réponses peuvent contenir n'importe quel type de données pouvant être transférées via HTTP.

Combien de données peuvent être stockées ?

En bref, beaucoup, au moins quelques centaines de mégaoctets, et potentiellement des centaines de gigaoctets ou plus. Les implémentations de navigateur varient, mais la quantité de stockage disponible dépend généralement de la quantité de stockage disponible sur l'appareil.

Créer et ouvrir un cache

Pour ouvrir un cache, utilisez la méthode caches.open(name) en transmettant le nom du cache comme paramètre unique. Si le cache nommé n'existe pas, il est créé. Cette méthode renvoie un Promise qui se résout avec l'objet Cache.

const cache = await caches.open('my-cache');
// do something with cache...

Ajouter à un cache

Il existe trois façons d'ajouter un élément à un cache : add, addAll et put. Les trois méthodes renvoient un Promise.

cache.add

Tout d'abord, il y a cache.add(). Elle accepte un paramètre, soit un Request, soit une URL (string). Elle envoie une requête au réseau et stocke la réponse dans le cache. Si la récupération échoue ou si le code d'état de la réponse ne se situe pas dans la plage 200, rien n'est stocké et Promise est rejeté. Notez que les requêtes inter-origines qui ne sont pas en mode CORS ne peuvent pas être stockées, car elles renvoient un status de 0. Ces requêtes ne peuvent être stockées qu'avec put.

// Retreive data.json from the server and store the response.
cache.add(new Request('/data.json'));

// Retreive data.json from the server and store the response.
cache.add('/data.json');

cache.addAll

Ensuite, il y a cache.addAll(). Il fonctionne de manière similaire à add(), mais prend un tableau d'objets ou d'URL Request (string). Cela fonctionne de la même manière que l'appel de cache.add pour chaque requête individuelle, sauf que Promise rejette toute requête individuelle qui n'est pas mise en cache.

const urls = ['/weather/today.json', '/weather/tomorrow.json'];
cache.addAll(urls);

Dans chacun de ces cas, une nouvelle entrée écrase toute entrée existante correspondante. Les mêmes règles de correspondance que celles décrites dans la section sur la récupération sont appliquées.

cache.put

Enfin, il existe cache.put(), qui vous permet de stocker une réponse du réseau ou de créer et de stocker votre propre Response. Elle prend deux paramètres. Le premier peut être un objet Request ou une URL (string). Le second doit être un Response, soit à partir du réseau, soit généré par votre code.

// Retrieve data.json from the server and store the response.
cache.put('/data.json');

// Create a new entry for test.json and store the newly created response.
cache.put('/test.json', new Response('{"foo": "bar"}'));

// Retrieve data.json from the 3rd party site and store the response.
cache.put('https://example.com/data.json');

La méthode put() est plus permissive que add() ou addAll(). Elle vous permet de stocker des réponses non CORS ou d'autres réponses dont le code d'état n'est pas compris dans la plage 200. Il écrasera toutes les réponses précédentes pour la même requête.

Créer des objets Request

Créez l'objet Request à l'aide d'une URL pour l'élément stocké:

const request = new Request('/my-data-store/item-id');

Utiliser des objets Response

Le constructeur d'objet Response accepte de nombreux types de données, y compris des Blob, des ArrayBuffer, des objets FormData et des chaînes.

const imageBlob = new Blob([data], {type: 'image/jpeg'});
const imageResponse = new Response(imageBlob);
const stringResponse = new Response('Hello world');

Vous pouvez définir le type MIME d'un Response en définissant l'en-tête approprié.

  const options = {
    headers: {
      'Content-Type': 'application/json'
    }
  }
  const jsonResponse = new Response('{}', options);

Si vous avez récupéré un Response et que vous souhaitez accéder à son corps, vous pouvez utiliser plusieurs méthodes d'assistance. Chacun renvoie un Promise qui se résout avec une valeur d'un autre type.

Méthode Description
arrayBuffer Renvoie un ArrayBuffer contenant le corps, sérialisé en octets.
blob Renvoie un objet Blob. Si le Response a été créé avec un Blob, ce nouveau Blob a le même type. Sinon, le Content-Type de l'Response est utilisé.
text Interpréte les octets du corps en tant que chaîne encodée en UTF-8.
json Interpréte les octets du corps en tant que chaîne encodée en UTF-8, puis tente de l'analyser en tant que JSON. Renvoie l'objet obtenu ou génère une exception TypeError si la chaîne ne peut pas être analysée en tant que fichier JSON.
formData Interpréte les octets du corps en tant que formulaire HTML, encodé en tant que multipart/form-data ou application/x-www-form-urlencoded. Renvoie un objet FormData ou génère une exception TypeError si les données ne peuvent pas être analysées.
body Renvoie un ReadableStream pour les données du corps.

Exemple :

const response = new Response('Hello world');
const buffer = await response.arrayBuffer();
console.log(new Uint8Array(buffer));
// Uint8Array(11) [72, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100]

Récupérer à partir d'un cache

Pour rechercher un élément dans un cache, vous pouvez utiliser la méthode match.

const response = await cache.match(request);
console.log(request, response);

Si request est une chaîne, le navigateur la convertit en Request en appelant new Request(request). La fonction renvoie un Promise qui se résout en Response si une entrée correspondante est trouvée, ou undefined dans le cas contraire.

Pour déterminer si deux Requests correspondent, le navigateur utilise plus que l'URL. Deux requêtes sont considérées comme différentes si elles ont des chaînes de requête, des en-têtes Vary ou des méthodes HTTP (GET, POST, PUT, etc.) différents.

Vous pouvez ignorer tout ou partie de ces éléments en transmettant un objet d'options en tant que deuxième paramètre.

const options = {
  ignoreSearch: true,
  ignoreMethod: true,
  ignoreVary: true
};

const response = await cache.match(request, options);
// do something with the response

Si plusieurs requêtes mises en cache correspondent, celle créée en premier est renvoyée. Si vous souhaitez récupérer toutes les réponses correspondantes, vous pouvez utiliser cache.matchAll().

const options = {
  ignoreSearch: true,
  ignoreMethod: true,
  ignoreVary: true
};

const responses = await cache.matchAll(request, options);
console.log(`There are ${responses.length} matching responses.`);

Pour plus de commodité, vous pouvez rechercher dans tous les caches en même temps à l'aide de caches.match() au lieu d'appeler cache.match() pour chaque cache.

Recherche en cours

L'API Cache ne permet pas de rechercher des requêtes ou des réponses, sauf les entrées correspondant à un objet Response. Toutefois, vous pouvez implémenter votre propre recherche à l'aide du filtrage ou en créant un indice.

Filtrage

Une façon d'implémenter votre propre recherche consiste à itérer sur toutes les entrées et à filtrer celles qui vous intéressent. Supposons que vous souhaitiez trouver tous les éléments dont les URL se terminent par .png.

async function findImages() {
  // Get a list of all of the caches for this origin
  const cacheNames = await caches.keys();
  const result = [];

  for (const name of cacheNames) {
    // Open the cache
    const cache = await caches.open(name);

    // Get a list of entries. Each item is a Request object
    for (const request of await cache.keys()) {
      // If the request URL matches, add the response to the result
      if (request.url.endsWith('.png')) {
        result.push(await cache.match(request));
      }
    }
  }

  return result;
}

Vous pouvez ainsi utiliser n'importe quelle propriété des objets Request et Response pour filtrer les entrées. Notez que cette opération est lente si vous effectuez une recherche sur de grands ensembles de données.

Créer un index

L'autre méthode d'implémenter votre propre recherche consiste à gérer un index distinct des entrées pouvant être recherchées et à stocker l'index dans IndexedDB. Étant donné qu'il s'agit du type d'opération pour lequel IndexedDB a été conçu, il offre de bien meilleures performances avec un grand nombre d'entrées.

Si vous stockez l'URL de l'Request avec les propriétés pouvant être recherchées, vous pouvez facilement récupérer la bonne entrée de cache après avoir effectué la recherche.

Supprimer un élément

Pour supprimer un élément d'un cache:

cache.delete(request);

La requête peut être une chaîne Request ou une URL. Cette méthode utilise également le même objet d'options que cache.match, ce qui vous permet de supprimer plusieurs paires Request/Response pour la même URL.

cache.delete('/example/file.txt', {ignoreVary: true, ignoreSearch: true});

Supprimer un cache

Pour supprimer un cache, appelez caches.delete(name). Cette fonction renvoie un Promise qui renvoie vers true si le cache existait et a été supprimé, ou false dans le cas contraire.

Merci

Merci à Mat Scales, qui a écrit la version originale de cet article, qui est apparu pour la première fois sur WebFundamentals.