Experiencias de navegación instantánea

Complementar las técnicas de carga previa tradicionales con service workers

Realizar una tarea en un sitio suele implicar varios pasos. Por ejemplo, comprar un producto en un sitio web de comercio electrónico podría implicar buscar un producto, elegir un artículo de la lista de resultados, agregar el artículo al carrito y completar la operación mediante el pago.

En términos técnicos, desplazarse por diferentes páginas significa realizar una solicitud de navegación. Como regla general, no es conveniente usar encabezados Cache-Control de larga duración para almacenar en caché la respuesta HTML de una solicitud de navegación. Por lo general, deberían satisfacerse a través de la red, con Cache-Control: no-cache, para garantizar que el código HTML, junto con la cadena de solicitudes de red posteriores, estén actualizados (razonablemente). Lamentablemente, tener que competir con la red cada vez que el usuario navega a una página nueva significa que la navegación puede ser lenta. Como mínimo, significa que no será rápida confiablemente.

Para acelerar estas solicitudes, si puedes anticipar la acción del usuario, puedes solicitar estas páginas y recursos con anticipación, y conservarlos en la caché por un período breve hasta que el usuario haga clic en estos vínculos. Esta técnica se denomina carga previa y, por lo general, se implementa agregando etiquetas <link rel="prefetch"> a las páginas, que indican el recurso que se carga previamente.

En esta guía, exploraremos diferentes formas en las que puedes usar los service worker como complemento de las técnicas tradicionales de carga previa.

Casos de producción

MercadoLibre es el sitio de comercio electrónico más grande de Latinoamérica. Para acelerar las navegaciones, insertan de forma dinámica etiquetas <link rel="prefetch"> en algunas partes del flujo. Por ejemplo, en las páginas de fichas, recuperan la siguiente página de resultados en cuanto el usuario se desplaza hasta la parte inferior de la ficha:

Captura de pantalla de la ficha de MercadoLibre de las páginas uno y dos, y una etiqueta de carga previa de vínculo que conecta ambas.

Los archivos precargados se solicitan con la prioridad "Más baja" y se almacenan en la caché de HTTP o en la memoria caché (según si el recurso se puede almacenar en caché o no) durante un período que varía según el navegador. Por ejemplo, a partir de Chrome 85, este valor es 5 minutos. Los recursos se mantienen alrededor de cinco minutos, después de lo cual se aplican las reglas Cache-Control normales para el recurso.

El uso del almacenamiento en caché del service worker puede ayudarte a extender la vida útil de los recursos de carga previa más allá del período de cinco minutos.

Por ejemplo, el portal italiano de deportes Virgilio Sport usa service workers para cargar previamente las publicaciones más populares en su página principal. También usan la API de Network Information para evitar la carga previa para usuarios que están en una conexión 2G.

Logotipo de Virgilio Sport.

Como resultado, más de 3 semanas de observación de Virgilio Sport observó que los tiempos de carga de la navegación hacia los artículos aumentaron un 78%, y la cantidad de impresiones de artículos aumentó un 45%.

Captura de pantalla de las páginas de la página principal y de artículos de Virgilio Sport, con métricas de impacto después de la carga previa.

Implementa el almacenamiento previo en caché con Workbox

En la siguiente sección, usaremos Workbox para mostrar cómo implementar diferentes técnicas de almacenamiento en caché en el service worker que se pueden usar como complemento de <link rel="prefetch">, o incluso para reemplazarlo, mediante la delegación de esta tarea por completo al service worker.

1. Almacenamiento previo de páginas estáticas y subrecursos de páginas

El almacenamiento previo en caché es la capacidad del service worker de guardar archivos en la caché durante la instalación.

En los siguientes casos, el almacenamiento previo en caché se usa para lograr un objetivo similar al de la carga previa, es decir, agilizar las navegaciones.

Almacenamiento previo en caché de páginas estáticas

En el caso de las páginas que se generan en el tiempo de compilación (p.ej., about.html y contact.html) o en sitios completamente estáticos, se pueden agregar simplemente los documentos del sitio a la lista de precaché, de modo que ya estén disponibles en la caché cada vez que el usuario acceda a ellas:

workbox.precaching.precacheAndRoute([
  {url: '/about.html', revision: 'abcd1234'},
  // ... other entries ...
]);

Almacenamiento previo en caché de subrecursos de la página

El almacenamiento previo en caché de los recursos estáticos que las diferentes secciones del sitio podrían usar (p.ej., JavaScript, CSS, etc.) es una práctica recomendada general y puede impulsar aún más las situaciones de carga previa.

Para acelerar la navegación en un sitio de comercio electrónico, puedes usar etiquetas <link rel="prefetch"> en las páginas de fichas y hacer una carga previa de las páginas de detalles de los primeros productos de una página de ficha. Si ya almacenaste en caché los subrecursos de la página del producto, esto puede hacer que la navegación sea aún más rápida.

Para ello, sigue estos pasos:

  • Agrega una etiqueta <link rel="prefetch"> a la página:
 <link rel="prefetch" href="/phones/smartphone-5x.html" as="document">
  • Agrega los subrecursos de la página a la lista de almacenamiento previo en caché en el service worker:
workbox.precaching.precacheAndRoute([
  '/styles/product-page.ac29.css',
  // ... other entries ...
]);

2. Cómo extender la vida útil de los recursos de carga previa

Como se mencionó antes, <link rel="prefetch"> recupera y mantiene los recursos en la caché HTTP durante un tiempo limitado. Después de ese tiempo, se aplican las reglas Cache-Control para un recurso. A partir de Chrome 85, este valor es de 5 minutos.

Los service workers te permiten extender la vida útil de las páginas de carga previa, al mismo tiempo que proporcionan el beneficio adicional de hacer que esos recursos estén disponibles para el uso sin conexión.

En el ejemplo anterior, se podría complementar el <link rel="prefetch"> que se usa para cargar previamente una página de producto con una estrategia de almacenamiento en caché en el entorno de ejecución de Workbox.

Para implementarlo, sigue estos pasos:

  • Agrega una etiqueta <link rel="prefetch"> a la página:
 <link rel="prefetch" href="/phones/smartphone-5x.html" as="document">
  • Implementa una estrategia de almacenamiento en caché del entorno de ejecución en el service worker para los siguientes tipos de solicitudes:
new workbox.strategies.StaleWhileRevalidate({
  cacheName: 'document-cache',
  plugins: [
    new workbox.expiration.Plugin({
      maxAgeSeconds: 30 * 24 * 60 * 60, // 30 Days
    }),
  ],
});

En este caso, habilitamos una estrategia de revalidación activa. En esta estrategia, se pueden solicitar páginas desde la caché y la red en paralelo. La respuesta proviene de la caché si está disponible; de lo contrario, proviene de la red. La caché siempre se mantiene actualizada con la respuesta de la red en cada solicitud correcta.

3. Delega la carga previa al service worker

En la mayoría de los casos, el mejor enfoque es usar <link rel="prefetch">. La etiqueta es una sugerencia de recursos diseñada para que la carga previa sea lo más eficiente posible.

Sin embargo, en algunos casos, es mejor delegar esta tarea por completo al service worker. Por ejemplo, para cargar previamente los primeros productos en una página de fichas de productos procesadas por el cliente, es posible que se necesite insertar varias etiquetas <link rel="prefetch"> de forma dinámica en la página, según una respuesta de la API. Esto puede consumir tiempo temporalmente en el subproceso principal de la página y dificultar la implementación.

En casos como este, usa una "estrategia de comunicación de página a service worker" para delegar la tarea de carga previa por completo al service worker. Este tipo de comunicación se puede lograr con worker.postMessage():

Ícono de una página que establece comunicación bidireccional con un service worker.

El paquete de la ventana del cuadro de trabajo simplifica este tipo de comunicación, ya que abstrae muchos detalles de la llamada subyacente que se realiza.

La carga previa con la ventana de Workbox se puede implementar de la siguiente manera:

  • En la página, llama al service worker y pásale el tipo de mensaje y la lista de URLs que quieres cargar previamente:
const wb = new Workbox('/sw.js');
wb.register();

const prefetchResponse = await wb.messageSW({type: 'PREFETCH_URLS', urls: […]});
  • En el service worker, implementa un controlador de mensajes con el objetivo de emitir una solicitud fetch() para cada URL que se vaya a cargar previamente:
addEventListener('message', (event) => {
  if (event.data.type === 'PREFETCH_URLS') {
    // Fetch URLs and store them in the cache
  }
});