Entrega

Un aspecto clave de las aplicaciones web progresivas es que son confiables: pueden cargar recursos rápidamente, mantener a los usuarios interesados y proporcionar comentarios de inmediato, incluso en condiciones de redes deficientes. ¿Cómo puede ser posible? Gracias al evento fetch del service worker.

El evento de recuperación

Browser Support

  • Chrome: 40.
  • Edge: 17.
  • Firefox: 44.
  • Safari: 11.1.

Source

El evento fetch nos permite interceptar cada solicitud de red que realiza la PWA en el alcance del service worker, tanto para las solicitudes del mismo origen como para las de origen cruzado. Además de las solicitudes de navegación y de recursos, la recuperación desde un Service Worker instalado permite que las visitas a la página después de la primera carga de un sitio se rendericen sin llamadas de red.

El controlador fetch recibe todas las solicitudes de una app, incluidas las URLs y los encabezados HTTP, y permite que el desarrollador de la app decida cómo procesarlas.

El service worker se encuentra entre el cliente y la red.

Tu service worker puede reenviar una solicitud a la red, responder con una respuesta almacenada en caché previamente o crear una respuesta nueva. La decisión es tuya. Este es un ejemplo simple:

self.addEventListener("fetch", event => {
    console.log(`URL requested: ${event.request.url}`);
});

Cómo responder a una solicitud

Cuando una solicitud llega a tu service worker, puedes hacer dos cosas: ignorarla, lo que permite que vaya a la red, o responderla. Responder a las solicitudes desde tu service worker es la forma en que puedes elegir qué se devuelve a tu AWP y cómo se devuelve, incluso cuando el usuario está sin conexión.

Para responder a una solicitud entrante, llama a event.respondWith() desde un controlador de eventos fetch, de la siguiente manera:

// fetch event handler in your service worker file
self.addEventListener("fetch", event => {
    const response = .... // a response or a Promise of response
    event.respondWith(response);
});

Debes llamar a respondWith() de forma síncrona y devolver un objeto Response. Sin embargo, no puedes llamar a respondWith() después de que finalice el controlador de eventos de recuperación, como dentro de una llamada asíncrona. Si necesitas esperar la respuesta completa, puedes pasar una promesa a respondWith() que se resuelva con una respuesta.

Cómo crear respuestas

Gracias a la API de Fetch, puedes crear respuestas HTTP en tu código JavaScript, y esas respuestas se pueden almacenar en caché con la API de Cache Storage y devolverse como si provinieran de un servidor web.

Para crear una respuesta, crea un objeto Response nuevo y establece su cuerpo y opciones, como el estado y los encabezados:

const simpleResponse = new Response("Body of the HTTP response");

const options = {
   status: 200,
   headers: {
    'Content-type': 'text/html'
   }
};
const htmlResponse = new Response("<b>HTML</b> content", options)

Cómo responder desde la caché

Ahora que sabes cómo entregar respuestas HTTP desde un service worker, es momento de usar la interfaz de Caching Storage para almacenar recursos en el dispositivo.

Puedes usar la API de almacenamiento en caché para verificar si la solicitud recibida de la PWA está disponible en la caché y, si lo está, responder a respondWith() con ella. Para ello, primero debes buscar dentro de la caché. La función match(), disponible en la interfaz caches de nivel superior, busca todos los almacenes en tu origen o en un solo objeto de caché abierto.

La función match() recibe una solicitud HTTP o una URL como argumento y devuelve una promesa que se resuelve con la respuesta asociada a la clave correspondiente.

// Global search on all caches in the current origin
caches.match(urlOrRequest).then(response => {
   console.log(response ? response : "It's not in the cache");
});

// Cache-specific search
caches.open("pwa-assets").then(cache => {
  cache.match(urlOrRequest).then(response => {
    console.log(response ? response : "It's not in the cache");
  });
});

Estrategias de almacenamiento en caché

Entregar archivos solo desde la caché del navegador no se adapta a todos los casos de uso. Por ejemplo, el usuario o el navegador pueden expulsar la caché. Por eso, debes definir tus propias estrategias para entregar recursos de tu PWA. No estás limitado a una sola estrategia de almacenamiento en caché. Puedes definir diferentes para diferentes patrones de URL. Por ejemplo, puedes tener una estrategia para los recursos de la IU mínimos, otra para las llamadas a la API y una tercera para las URLs de imágenes y datos. Para ello, lee event.request.url en ServiceWorkerGlobalScope.onfetch y analízalo con expresiones regulares o un patrón de URL. (En el momento de la redacción, el patrón de URL no se admite en todas las plataformas).

Las estrategias más comunes son las siguientes:

Caché primero
Primero busca una respuesta almacenada en caché y recurre a la red si no encuentra una.
Prioridad para la red
Primero solicita una respuesta de la red y, si no se devuelve ninguna, busca una respuesta en la caché.
Stale While Revalidate
Entrega una respuesta desde la caché y, en segundo plano, solicita la versión más reciente y la guarda en la caché para la próxima vez que se solicite el recurso.
Solo en la red
Siempre responde con una respuesta de la red o muestra un error. Nunca se consulta la caché.
Solo caché
Siempre responde con una respuesta de la caché o muestra un error. Nunca se consultará la red. Los recursos que se publicarán con esta estrategia deben agregarse a la caché antes de que se soliciten.

Caché primero

Con esta estrategia, el service worker busca la solicitud coincidente en la caché y devuelve la respuesta correspondiente si está almacenada en caché. De lo contrario, recupera la respuesta de la red (y, de manera opcional, actualiza la caché para llamadas futuras). Si no hay una respuesta de caché ni una respuesta de red, la solicitud generará un error. Dado que publicar recursos sin acceder a la red suele ser más rápido, esta estrategia prioriza el rendimiento por sobre la actualización.

La estrategia Cache First

self.addEventListener("fetch", event => {
   event.respondWith(
     caches.match(event.request)
     .then(cachedResponse => {
       // It can update the cache to serve updated content on the next request
         return cachedResponse || fetch(event.request);
     }
   )
  )
});

Prioridad de la red

Esta estrategia es el reflejo de la estrategia Cache First: comprueba si la solicitud se puede satisfacer desde la red y, si no es así, intenta recuperarla de la caché. Almacenamiento en caché primero Si no hay respuesta de red ni respuesta de caché, se producirá un error en la solicitud. Obtener la respuesta de la red suele ser más lento que obtenerla de la caché. Esta estrategia prioriza el contenido actualizado en lugar del rendimiento.

La estrategia Network First

self.addEventListener("fetch", event => {
   event.respondWith(
     fetch(event.request)
     .catch(error => {
       return caches.match(event.request) ;
     })
   );
});

Inactivo mientras se revalida

La estrategia stale while revalidate devuelve una respuesta almacenada en caché de inmediato y, luego, verifica si hay una actualización en la red y reemplaza la respuesta almacenada en caché si se encuentra una. Esta estrategia siempre realiza una solicitud de red, ya que, incluso si se encuentra un recurso almacenado en caché, intentará actualizar lo que había en la caché con lo que se recibió de la red para usar la versión actualizada en la próxima solicitud. Por lo tanto, esta estrategia te permite beneficiarte de la rapidez de la estrategia de caché primero y actualizar la caché en segundo plano.

La estrategia de contenido inactivo durante la revalidación

self.addEventListener('fetch', event => {
  event.respondWith(
    caches.match(event.request).then(cachedResponse => {
        const networkFetch = fetch(event.request).then(response => {
          // update the cache with a clone of the network response
          const responseClone = response.clone()
          caches.open(url.searchParams.get('name')).then(cache => {
            cache.put(event.request, responseClone)
          })
          return response
        }).catch(function (reason) {
          console.error('ServiceWorker fetch failed: ', reason)
        })
        // prioritize cached response over network
        return cachedResponse || networkFetch
      }
    )
  )
})

Solo de red

La estrategia solo de red es similar al comportamiento de los navegadores sin un service worker o la API de Cache Storage. Las solicitudes solo devolverán un recurso si se puede recuperar de la red. Esto suele ser útil para recursos como las solicitudes de API solo en línea.

La estrategia Solo en la red

Solo caché

La estrategia de solo caché garantiza que las solicitudes nunca vayan a la red; todas las solicitudes entrantes se responden con un elemento de caché completado previamente. El siguiente código usa el controlador de eventos fetch con el método match del almacenamiento en caché para responder solo desde la caché:

self.addEventListener("fetch", event => {
   event.respondWith(caches.match(event.request));
});

Es una estrategia de solo caché.

Estrategias personalizadas

Si bien las anteriores son estrategias de almacenamiento en caché comunes, tú eres responsable de tu Service Worker y de cómo se controlan las solicitudes. Si ninguno de estos se adapta a tus necesidades, crea uno propio.

Por ejemplo, puedes usar una estrategia de red primero con un tiempo de espera para priorizar el contenido actualizado, pero solo si la respuesta aparece dentro de un umbral que establezcas. También puedes combinar una respuesta almacenada en caché con una respuesta de red y compilar una respuesta compleja desde el service worker.

Actualización de recursos

Mantener actualizados los recursos almacenados en caché de tu PWA puede ser un desafío. Si bien la estrategia de revalidación mientras está inactivo es una forma de hacerlo, no es la única. En el capítulo de actualización, aprenderás diferentes técnicas para mantener actualizados el contenido y los recursos de tu app.

Recursos