Entrega

Un aspecto clave de las Progressive Web Apps es que son confiables. Pueden cargar los recursos 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.

Navegadores compatibles

  • Chrome: 40.
  • Límite: 17.
  • Firefox: 44.
  • Safari: 11.1.

Origen

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

El controlador fetch recibe todas las solicitudes de una aplicación, incluidas las URLs y los encabezados HTTP, y permite al desarrollador de la app decidir 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 previamente almacenada en caché o crear una respuesta nueva. La decisión es tuya. A continuación, te mostramos un ejemplo sencillo:

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, tienes dos opciones: Puede ignorarla, lo que le permite ir a la red, o puede responder a ella. Responder a las solicitudes desde tu service worker es la forma en que puedes elegir qué y cómo se devuelve a tu AWP, 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 debes mostrar un objeto Response. Sin embargo, no puedes llamar a respondWith() después de que finalice el controlador de eventos 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.

Crea respuestas

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

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

Responder desde la caché

Ahora que sabes cómo entregar respuestas HTTP desde un service worker, Es momento 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 hacer eso, 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é

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 ello, debes definir tus propias estrategias para publicar recursos para tu AWP. No estás limitado a una estrategia de almacenamiento en caché. Puedes definir diferentes para distintos patrones de URL. Por ejemplo, puedes tener una estrategia para los recursos mínimos de IU, otra para las llamadas a la API y una tercera para las URLs de imagen 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 una respuesta almacenada en caché y, si no encuentra una, recurre a la red.
Primero la red
Solicita una respuesta de la red primero y, si no se muestra ninguna, verifica la respuesta en la caché.
Inactiva durante la revalidación
Entrega una respuesta desde 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 de red
Siempre responde con una respuesta de la red o con errores. Nunca se consulta la caché.
Solo caché
Siempre responde con una respuesta de la caché o errores. Nunca se consultará 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 muestra la respuesta correspondiente si está almacenada en caché. De lo contrario, recupera la respuesta de la red (opcionalmente, actualiza la caché para futuras llamadas). Si no hay una respuesta de caché ni una respuesta de red, se generará un error en la solicitud. 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 actualidad.

La estrategia de almacenamiento en caché primero

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

Primero la red

Esta estrategia es la duplicación de la estrategia de caché primero. verifica si la solicitud se puede llevar a cabo desde la red y, si no puede hacerlo, intenta recuperarla desde la caché. Como la caché primero. 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 lenta que obtenerla de la caché. Esta estrategia prioriza el contenido actualizado en lugar del rendimiento.

La estrategia que prioriza la red

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

Inactiva durante la revalidación

La estrategia inactiva durante la revalidación muestra una respuesta almacenada en caché de inmediato. Luego, comprueba si hay actualizaciones en la red 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 lo que estaba 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 publicación rápida de la estrategia de almacenamiento en caché primero y actualizar la caché en segundo plano.

La estrategia inactiva 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 de solo red es similar a cómo se comportan los navegadores sin un service worker o 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 red

Solo caché

La estrategia de solo almacenamiento en 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 anteriores son estrategias comunes de almacenamiento en caché, tú estás a cargo del service worker y de la manera en que se manejan las solicitudes. Si ninguna de estas opciones satisface tus necesidades, crea una propia.

Por ejemplo, puedes usar una estrategia centrada en la red con un tiempo de espera para priorizar el contenido actualizado, pero solo si la respuesta aparece dentro del umbral que estableciste. 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 recursos

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

Recursos