Activo

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

El evento de recuperación

Navegadores compatibles

  • 40
  • 17
  • 44
  • 11.1

Origen

El evento fetch nos permite interceptar todas las solicitudes de red que envía la AWP dentro del alcance del service worker, para solicitudes de mismo origen y de origen cruzado. Además de las solicitudes de navegación y recursos, la recuperación de un service worker instalado permite que las visitas a la página posteriores a 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 ubica entre el cliente y la red.

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

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

Cómo responder a una solicitud

Cuando llega una solicitud a tu service worker, hay dos cosas que puedes hacer: puedes ignorarla y dejarla pasar a la red, o bien responder a ella. Si respondes a las solicitudes desde tu service worker, podrás elegir qué se mostrará en tu AWP y cómo se mostrará, incluso cuando el usuario no tenga 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 mostrar un objeto Response. Sin embargo, no puedes llamar a respondWith() una vez que finalice el controlador del evento de recuperación, como en 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 pueden almacenarse en caché con la API de Cache Storage y mostrarse como si provinieran de un servidor web.

Para crear una respuesta, crea un nuevo objeto Response y configura 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)

Respuestas desde la caché

Ahora que sabes cómo entregar respuestas HTTP desde un service worker, es hora de usar la interfaz de almacenamiento en caché para almacenar recursos en el dispositivo.

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

La función match() recibe una solicitud HTTP o una URL como argumento y muestra 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é

La entrega de 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 publicar los elementos de tu AWP. No hay restricciones en cuanto a una estrategia de almacenamiento en caché. Puedes definir diferentes valores para distintos patrones de URL. Por ejemplo, puedes tener una estrategia para los recursos mínimos de la IU, 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 mediante expresiones regulares o un patrón de URL. (Al momento de la redacción, el patrón de URL no es compatible con todas las plataformas).

Las estrategias más comunes son las siguientes:

Caché primero
Busca primero una respuesta almacenada en caché y recurre a la red si no se encuentra una.
Primero la red
Solicita una respuesta de la red primero y, si no se muestra ninguna, comprueba la respuesta en la caché.
Inactiva durante la revalidación
Entrega una respuesta de la caché, mientras que 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 red
Siempre responde con una respuesta de la red o con un error. Nunca se consulta la caché.
Solo caché
Siempre responde con una respuesta de la caché o con errores. Nunca se consultará a la red. Los recursos que se publicarán con esta estrategia deben agregarse a la caché antes de que se soliciten.

Primero en caché

Con esta estrategia, el service worker busca la solicitud coincidente en la caché y devuelve la respuesta correspondiente si está almacenada en la caché. De lo contrario, recupera la respuesta de la red (opcionalmente, 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 la publicación de recursos sin ir a la red suele ser más rápida, esta estrategia prioriza el rendimiento sobre la actualización.

La estrategia Almacenar primero en caché

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 red

Esta estrategia es la duplicación de la estrategia Almacenar caché primero; comprueba si la solicitud se puede llevar a cabo desde la red y, si no puede hacerlo, intenta recuperarla de la caché. Primero, indica el uso de la caché. Si no hay una respuesta de red ni una respuesta de caché, la solicitud generará un error. 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) ;
     })
   );
});

Inactiva durante la revalidación

La estrategia de estado inactivo durante la revalidación muestra inmediatamente una respuesta almacenada en caché y, luego, comprueba la red en busca de una actualización y reemplaza la respuesta almacenada en caché, si se encuentra una. Esta estrategia siempre realiza una solicitud de red porque, incluso si se encuentra un recurso almacenado en caché, intentará actualizar el contenido de la caché con lo que se recibió de la red para usar la versión actualizada en la siguiente solicitud. Por lo tanto, esta estrategia proporciona una forma de aprovechar la entrega rápida de la estrategia que prioriza la caché y actualizar la caché en segundo plano.

El inactivo y revalida la estrategia

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 de solo red es similar a cómo se comportan los navegadores sin un service worker o sin la API de Cache Storage. Las solicitudes solo mostrarán un recurso si se puede recuperar de la red. Esto suele ser útil para recursos como las solicitudes a la API solo en línea.

La estrategia Solo de 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é prepropagado. El siguiente código usa el controlador de eventos fetch con el método match del almacenamiento en caché para responder solo a la caché:

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

Estrategia de solo caché.

Estrategias personalizadas

Si bien las opciones anteriores son estrategias comunes de almacenamiento en caché, tú estás a cargo del service worker y de la forma en que se manejan las solicitudes. Si ninguna de estas opciones se ajusta a tus necesidades, crea una propia.

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

Actualizando elementos

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

Recursos