Estrategias para el almacenamiento en caché de service worker

Hasta ahora, solo se habían hecho menciones y pequeños fragmentos de código del Interfaz de Cache. Para usar los service workers de forma eficaz, es necesario adoptar una o más estrategias lo que requiere tener un poco de familiaridad con la interfaz Cache.

Una estrategia de almacenamiento en caché es una interacción entre el evento fetch de un service worker y la interfaz Cache. La forma en que se escribe una estrategia de almacenamiento en caché depende Por ejemplo, puede ser preferible manejar las solicitudes de recursos estáticos de manera diferente a los documentos y esto afecta la composición de una estrategia de almacenamiento en caché.

Antes de pasar a las estrategias, Veamos qué no es la interfaz Cache, qué es y un resumen rápido de algunos de los métodos que ofrece para administrar cachés de service worker.

La interfaz Cache en comparación con la caché HTTP

Si no trabajaste con la interfaz Cache antes, puede ser tentador pensar que es lo mismo que, o, al menos, con la caché HTTP. Este no es el caso.

  • La interfaz Cache es un mecanismo de almacenamiento en caché completamente independiente de la caché HTTP.
  • Cualquiera Cache-Control que utilizas para influir en la caché HTTP no influye en los recursos que se almacenan en la interfaz Cache.

Considera que las cachés del navegador son en capas. La caché HTTP es una caché de bajo nivel impulsada por pares clave-valor con directivas expresadas en encabezados HTTP.

Por el contrario, la interfaz Cache es una caché de alto nivel controlada por una API de JavaScript. Esto ofrece más flexibilidad que cuando se usan pares clave-valor HTTP relativamente simplista y es la mitad de lo que posibilita las estrategias de almacenamiento en caché. Algunos métodos de API importantes para las cachés de service worker son los siguientes:

  • CacheStorage.open para crear una nueva instancia de Cache.
  • Cache.add y Cache.put para almacenar respuestas de la red en una caché de service worker.
  • Cache.match para ubicar una respuesta almacenada en caché en una instancia de Cache.
  • Cache.delete para quitar una respuesta almacenada en caché de una instancia de Cache.

Esos son solo algunos. Hay otros métodos útiles, pero estas son las básicas que verás más adelante en esta guía.

El modesto evento de fetch

La otra mitad de una estrategia de almacenamiento en caché Evento fetch. Hasta ahora, en esta documentación han escuchado algo sobre “interceptar solicitudes de red”, y el evento fetch dentro de un service worker es donde sucede lo siguiente:

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

Este es un ejemplo de juguete que puedas ver en acción por tu cuenta, pero nos da un vistazo a lo que pueden hacer los service workers. El código anterior hace lo siguiente:

  1. Inspecciona la propiedad destination de la solicitud para ver si se trata de una solicitud de imagen.
  2. Si la imagen está en la caché del service worker, publícala desde allí. Si no, recupera la imagen de la red almacenar la respuesta en la caché y mostrar la respuesta de la red.
  3. Todas las demás solicitudes se pasan por el service worker sin interacción con la caché.

El objeto event de una recuperación contiene un Propiedad request información útil que te ayudará a identificar el tipo de cada solicitud:

  • url, que es la URL de la solicitud de red que el evento fetch controla en ese momento.
  • method, que es el método de solicitud (p.ej., GET o POST).
  • mode, que describe el modo de la solicitud. A menudo, se usa un valor de 'navigate' para distinguir las solicitudes de documentos HTML de otras solicitudes.
  • destination, que describe el tipo de contenido que se solicita de una manera que evita usar la extensión de archivo del elemento solicitado.

Una vez más, asynchrony es el nombre del juego. Recordarás que el evento install ofrece un event.waitUntil que toma una promesa y espera a que se resuelva antes de continuar con la activación. El evento fetch ofrece una Método event.respondWith que puedes usar para devolver el resultado de una operación Solicitud de fetch o una respuesta devuelta por el Cache Método match.

Estrategias de almacenamiento en caché

Ahora que estás familiarizado con las instancias de Cache y el controlador de eventos fetch, Ahora veremos algunas estrategias de almacenamiento en caché de service workers. Si bien las posibilidades son prácticamente infinitas, esta guía se centrará en las estrategias incluidas en Workbox, para que puedas tener una idea de lo que pasa en la parte interna de Workbox.

Solo caché

Muestra el flujo desde la página hasta el service worker y la caché.

Comencemos con una estrategia simple de almacenamiento en caché que llamaremos “Solo caché”. Cuando el service worker controla la página, las solicitudes coincidentes solo irán a la caché. Esto significa que cualquier elemento en caché deberá almacenarse previamente en caché para estar disponible para que el patrón funcione, y que esos recursos nunca se actualizarán en la caché hasta que se actualice el 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;
  }
});

Arriba, un array de elementos se almacena previamente en caché durante la instalación. Cuando el service worker controla las recuperaciones, verificamos si la URL de la solicitud controlada por el evento fetch está en el array de recursos previamente almacenados en caché. Si es así, tomamos el recurso de la caché y omitimos la red. Otras solicitudes pasan a la red, y solo la red. Para ver esta estrategia en acción, mira esta demostración con la consola abierta.

Solo de red

Muestra el flujo desde la página hasta el service worker y la red.

Lo contrario a "Solo caché" es "Solo red" en la que una solicitud se pasa a través de un service worker a la red sin ninguna interacción con la caché del service worker. Esta es una buena estrategia para garantizar la actualidad del contenido (como lenguaje de marcado). pero la desventaja es que nunca funcionará cuando el usuario esté sin conexión.

Garantizar que una solicitud pase a través de la red solo significa que no llamas a event.respondWith para una solicitud coincidente. Si quieres ser explícito, Puedes poner una return; vacía en la devolución de llamada de eventos fetch para las solicitudes que quieras pasar a la red. Esto es lo que sucede en el menú “Solo caché” para solicitudes que no se almacenan previamente en caché.

Caché primero y recurrir a la red

Muestra el flujo desde la página hasta el service worker, la caché y, luego, la red si no está en la caché.

Esta estrategia es donde las cosas se involucran un poco más. Para las solicitudes coincidentes, el proceso es similar al siguiente:

  1. La solicitud llega a la caché. Si el recurso está en la caché, publícalo desde allí.
  2. Si la solicitud no está en la caché, ve a la red.
  3. Una vez finalizada la solicitud de red, agrégala a la caché. y, luego, devolverá la respuesta de la red.

Aquí hay un ejemplo de esta estrategia, que puedes probar en una demostración en 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;
  }
});

Aunque este ejemplo solo abarca imágenes, es una excelente estrategia para aplicar a todos los recursos estáticos (como CSS, JavaScript, imágenes y fuentes) en especial, aquellas con versiones de hash. Ofrece un aumento de velocidad para los recursos inmutables elevando cualquier verificación de actualización del contenido con el servidor que puede iniciar la caché HTTP. Lo más importante es que todos los elementos almacenados en caché estarán disponibles sin conexión.

Primero la red y recurrir a la caché

Muestra el flujo desde la página hasta el service worker, la red y, luego, al caché si la red no está disponible.

Si cambiaras la opción "Caché primero y red después" en la cabeza, verás el mensaje “Red primero, caché después” de palabras clave, que es lo que parece:

  1. Primero, ve a la red para realizar una solicitud y coloca la respuesta en la caché.
  2. Si posteriormente no tienes conexión, recurrirás a la última versión de esa respuesta en la caché.

Esta estrategia es ideal para solicitudes HTML o API cuando, mientras estás en línea, quieres la versión más reciente de un recurso, pero quieres otorgar acceso sin conexión a la versión disponible más reciente. Así es como se vería esto cuando se aplica a las solicitudes de 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;
  }
});

Puedes probarlo en una demostración. Primero, ve a la página. Es posible que debas volver a cargar la página antes de que la respuesta HTML se almacene en la caché. Luego, en tus herramientas para desarrolladores, simular una conexión sin conexión y vuelve a cargarla. Se entregará instantáneamente la última versión disponible desde la caché.

En situaciones en las que la capacidad sin conexión es importante pero necesita equilibrar esa capacidad con el acceso a la versión más reciente de un poco de lenguaje de marcado o datos de la API. "Primero la red, luego la caché" es una estrategia sólida que logra ese objetivo.

Inactiva durante la revalidación

Muestra el flujo desde la página hasta el service worker, la caché y, luego, de la red a la caché.

De las estrategias que vimos hasta ahora, se trata de la estrategia de “inactividad durante la revalidación” es el más complejo. En algunos aspectos, es similar a las dos últimas estrategias pero el procedimiento prioriza la velocidad de acceso a un recurso, al mismo tiempo que lo mantienen actualizado en segundo plano. Esta estrategia es más o menos así:

  1. En la primera solicitud de un recurso, recupéralo de la red. colocarla en la caché y devolver la respuesta de la red.
  2. En solicitudes posteriores, se publica primero el recurso desde la caché y, luego, “en segundo plano” volver a solicitarlo desde la red y actualizar la entrada de caché del elemento.
  3. Para las solicitudes posteriores, recibirás la última versión recuperada de la red que se colocó en la caché en el paso anterior.

Esta es una excelente estrategia para elementos que son algo importantes de mantener actualizados. pero no son cruciales. Piensa en elementos como avatares para un sitio de redes sociales. Se actualizan cuando los usuarios lo hacen, pero no es estrictamente necesaria la última versión en cada solicitud.

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

Puedes ver esto en acción en otra demostración en vivo. especialmente si prestas atención a la pestaña de red en las herramientas para desarrolladores de tu navegador, y su visor CacheStorage (si las herramientas para desarrolladores de tu navegador tienen estas herramientas).

¡Adelante a Workbox!

Este documento es el resumen de la revisión de la API del service worker, así como las APIs relacionadas, por lo que ya aprendiste lo suficiente sobre cómo usar los service workers de forma directa para empezar a experimentar con Workbox.