API de Cache: Guía rápida

Aprende a usar la API de Cache para que los datos de tu aplicación estén disponibles sin conexión.

La API de caché es un sistema para almacenar y recuperar solicitudes de red y sus respuestas correspondientes. Pueden ser solicitudes y respuestas normales creadas durante la ejecución de tu aplicación, o pueden crearse solo con el fin de almacenar datos para su uso posterior.

La API de Cache se creó para permitir que los service workers almacenen en caché las solicitudes de red a fin de que puedan proporcionar respuestas rápidas, sin importar la velocidad o disponibilidad de la red. Sin embargo, la API también se puede usar como un mecanismo de almacenamiento general.

¿En qué dispositivos está disponible?

La API de Cache está disponible en todos los navegadores modernos. Se expone a través de la propiedad global caches, por lo que puedes probar la presencia de la API con una detección de funciones simple:

const cacheAvailable = 'caches' in self;

Navegadores compatibles

  • 40
  • 16
  • 41
  • 11.1

Origen

Se puede acceder a la API de Cache desde una ventana, un iframe, un trabajador o un service worker.

Qué se puede almacenar

Las cachés solo almacenan pares de objetos Request y Response, que representan solicitudes y respuestas HTTP, respectivamente. Sin embargo, las solicitudes y respuestas pueden contener cualquier tipo de datos que se puedan transferir a través de HTTP.

¿Cuánto se puede almacenar?

En resumen, mucho, al menos un par de cientos de megabytes y, posiblemente, cientos de gigabytes o más. Las implementaciones del navegador varían, pero la cantidad de almacenamiento disponible, por lo general, se basa en la cantidad de almacenamiento disponible en el dispositivo.

Crea y abre una caché

Para abrir una caché, usa el método caches.open(name) y pasa el nombre de la caché como el parámetro único. Si la caché nombrada no existe, se crea. Este método muestra una Promise que se resuelve con el objeto Cache.

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

Agregar a una caché

Existen tres formas de agregar un elemento a una caché: add, addAll y put. Los tres métodos muestran un Promise.

cache.add

En primer lugar, se encuentra cache.add(). Toma un parámetro, ya sea un Request o una URL (string), realiza una solicitud a la red y almacena la respuesta en la caché. Si la recuperación falla o si el código de estado de la respuesta no está dentro del rango 200, no se almacenará nada y se rechazará Promise. Ten en cuenta que las solicitudes de origen cruzado que no están en modo CORS no se pueden almacenar porque muestran un status de 0. Estas solicitudes solo se pueden almacenar con 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

Luego, tenemos a cache.addAll(). Funciona de manera similar a add(), pero toma un array de objetos Request o URLs (string). Esto funciona de manera similar a llamar a cache.add para cada solicitud individual, excepto que Promise rechaza si alguna solicitud individual no se almacena en caché.

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

En cada uno de estos casos, una entrada nueva reemplaza cualquier entrada existente que coincida. Esto usa las mismas reglas de coincidencia que se describen en la sección sobre retrieving.

cache.put

Por último, está cache.put(), que te permite almacenar una respuesta de la red o crear y almacenar tu propio Response. Requiere dos parámetros. El primero puede ser un objeto Request o una URL (string). El segundo debe ser un Response, ya sea de la red o generado por tu código.

// 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');

El método put() es más permisivo que add() o addAll(), y te permitirá almacenar respuestas que no son de CORS, o bien otras respuestas en las que el código de estado de la respuesta no está en el rango 200. Reemplazará cualquier respuesta anterior para la misma solicitud.

Crea objetos Request

Crea el objeto Request con una URL para el elemento que se almacenará:

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

Trabaja con objetos Response

El constructor de objetos Response acepta muchos tipos de datos, incluidos Blob, ArrayBuffer, objetos FormData y cadenas.

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

Para establecer el tipo de MIME de un Response, configura el encabezado adecuado.

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

Si recuperaste un Response y deseas acceder a su cuerpo, puedes usar varios métodos auxiliares. Cada uno muestra un Promise que se resuelve con un valor de un tipo diferente.

Método Descripción
arrayBuffer Muestra un ArrayBuffer que contiene el cuerpo, serializado en bytes.
blob Muestra un objeto Blob. Si el Response se creó con un Blob, este nuevo Blob tiene el mismo tipo. De lo contrario, se usa el Content-Type de Response.
text Interpreta los bytes del cuerpo como una string codificada en UTF-8.
json Interpreta los bytes del cuerpo como una string codificada en UTF-8 y, luego, intenta analizarla como JSON. Muestra el objeto resultante o arroja una TypeError si la string no se puede analizar como JSON.
formData Interpreta los bytes del cuerpo como un formulario HTML, codificado como multipart/form-data o application/x-www-form-urlencoded. Muestra un objeto FormData o arroja una TypeError si no se pueden analizar los datos.
body Muestra un ReadableStream para los datos del cuerpo.

Por ejemplo:

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]

Recupera contenido de una caché

Para encontrar un elemento en una caché, puedes usar el método match.

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

Si request es una cadena, el navegador la convierte en Request llamando a new Request(request). La función muestra una Promise que se resuelve en Response si se encuentra una entrada coincidente o en undefined de lo contrario.

Para determinar si dos Requests coinciden, el navegador usa más que solo la URL. Dos solicitudes se consideran diferentes si tienen diferentes cadenas de consulta, encabezados Vary o métodos HTTP (GET, POST, PUT, etc.).

Para ignorar algunos o todos estos aspectos, puedes pasar un objeto de opciones como un segundo parámetro.

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

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

Si hay más de una solicitud almacenada en caché que coincide, se mostrará la que se creó primero. Si deseas recuperar todas las respuestas coincidentes, puedes usar 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.`);

Como acceso directo, puedes buscar en todas las cachés a la vez con caches.match() en lugar de llamar a cache.match() para cada caché.

Buscando

La API de Cache no proporciona una forma de buscar solicitudes o respuestas, excepto entradas coincidentes en un objeto Response. Sin embargo, puedes implementar tu propia búsqueda mediante el filtrado o la creación de un índice.

Filtros

Una forma de implementar tu propia búsqueda es iterar sobre todas las entradas y filtrar las que deseas. Supongamos que quieres encontrar todos los elementos que tienen una URL que termina en .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;
}

De esta manera, puedes usar cualquier propiedad de los objetos Request y Response para filtrar las entradas. Ten en cuenta que este proceso es lento si realizas búsquedas en grandes conjuntos de datos.

Crea un índice

La otra forma de implementar tu propia búsqueda es mantener un índice separado de entradas que se puedan buscar y almacenar el índice en IndexedDB. Dado que este es el tipo de operación para la que se diseñó IndexedDB, tiene un rendimiento mucho mejor con grandes cantidades de entradas.

Si almacenas la URL del Request junto con las propiedades de búsqueda, podrás recuperar fácilmente la entrada de caché correcta después de realizar la búsqueda.

Cómo borrar un elemento

Para borrar un elemento de una caché, haz lo siguiente:

cache.delete(request);

La solicitud puede ser Request o una string de URL. Este método también toma el mismo objeto de opciones que cache.match, lo que te permite borrar varios pares Request/Response para la misma URL.

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

Borra una caché

Para borrar una caché, llama a caches.delete(name). Esta función muestra una Promise que se resuelve en true si la caché existía y se borró, o en false.

Gracias

Gracias a Mat Scales, que escribió la versión original de este artículo, que apareció por primera vez en WebFundamentals.