Experiencias de navegación instantánea

Complementar técnicas tradicionales de carga previa 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 de pago.

En términos técnicos, desplazarse por diferentes páginas significa realizar una solicitud de navegación. Como regla general, no debes usar encabezados Cache-Control de larga duración para almacenar en caché la respuesta HTML para una solicitud de navegación. Por lo general, deben cumplirse a través de la red, con Cache-Control: no-cache, para garantizar que el HTML, junto con la cadena de solicitudes de red posteriores, estén actualizados (de manera razonable). Desafortunadamente, tener que ir contra la red cada vez que el usuario navega a una nueva página significa que cada navegación puede ser lenta, al menos significa que no será confiable rápida.

Para acelerar estas solicitudes, si puedes anticipar la acción del usuario, puedes solicitar estas páginas y recursos con anticipación, y guardarlos en la caché durante un breve período hasta que el usuario haga clic en estos vínculos. Esta técnica se denomina carga previa y se implementa comúnmente cuando se agregan etiquetas <link rel="prefetch"> a las páginas, que indican el recurso que se debe cargar de forma previa.

En esta guía, exploraremos diferentes maneras en las que se pueden usar los service workers como un 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 etiquetas <link rel="prefetch"> de forma dinámica en algunas partes del flujo. Por ejemplo, en las páginas de fichas, recuperan la página de resultados siguiente en cuanto el usuario se desplaza hasta la parte inferior de la ficha:

Captura de pantalla de las páginas de fichas uno y dos de MercadoLibre, 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é HTTP o en la caché de memoria (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 de 5 minutos. Los recursos se conservan durante cinco minutos. Luego, se aplican las reglas normales de Cache-Control para el recurso.

Usar el 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 de deportes italiano Virgilio Sport utiliza 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 de usuarios que tienen una conexión 2G.

Logotipo de Virgilio Sport.

Como resultado, durante 3 semanas de observación, Virgilio Sport observó que los tiempos de carga de la navegación a los artículos mejoraron un 78% y la cantidad de impresiones de artículos aumentó en un 45%.

Captura de pantalla de la página principal y las páginas de artículos de Virgilio Sport, con las 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 como reemplazo, delegando esta tarea por completo al service worker.

1. Almacena previamente en caché las páginas estáticas y los subrecursos de páginas.

El almacenamiento previo en caché es la capacidad del service worker de guardar archivos en la caché mientras se instala.

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

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

En el caso de las páginas que se generan en el momento de la compilación (p.ej., about.html, contact.html) o en sitios completamente estáticos, es posible agregar los documentos del sitio a la lista de almacenamiento previo en caché para que ya estén disponibles en la caché cada vez que el usuario acceda a ellos:

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

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

El almacenamiento previo en caché de los elementos estáticos que pueden usar las diferentes secciones del sitio (p.ej., JavaScript, CSS, etc.) es una práctica recomendada general y puede proporcionar un impulso adicional en 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 para cargar previamente las páginas de detalles de los productos para los primeros productos de una página de fichas. Si ya almacenaste previamente 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 caché previa en el service worker:
workbox.precaching.precacheAndRoute([
  '/styles/product-page.ac29.css',
  // ... other entries ...
]);

2. Extiende la vida útil de los recursos de carga previa

Como se mencionó antes, <link rel="prefetch"> recupera y conserva recursos en la caché HTTP durante un período limitado. Después de ese período, 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 y, al mismo tiempo, 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 precargar una página de producto con una estrategia de almacenamiento en caché en tiempo de ejecución de Workbox.

Para implementarlo:

  • 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é en tiempo 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, hemos optado por usar una estrategia de inactividad durante la revalidación. En esta estrategia, las páginas se pueden solicitar desde la caché y desde 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 con cada solicitud exitosa.

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, sería mejor delegar esta tarea por completo al service worker. Por ejemplo, para cargar previamente los primeros productos en una página de ficha de producto renderizada del cliente, es posible que se deba insertar varias etiquetas <link rel="prefetch"> de forma dinámica en la página según la respuesta de la API. Esto puede consumir temporalmente tiempo 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 completa al service worker. Este tipo de comunicación se puede lograr con worker.postMessage():

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

El paquete Workbox Window simplifica este tipo de comunicación y abstrae muchos detalles de la llamada subyacente que se realiza.

La carga previa con la ventana 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 URL que se deben 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 para emitir una solicitud fetch() para cada URL que se solicite previamente:
addEventListener('message', (event) => {
  if (event.data.type === 'PREFETCH_URLS') {
    // Fetch URLs and store them in the cache
  }
});