Habilitar soporte sin conexión en aplicaciones web con Service Workers
Los workers de servicio y la API de caché hacen que un sitio se cargue al instante y sigan funcionando sin conexión. Escribir el worker, registrarlo y elegir estrategias de caché.
Fuentes: ☕ JavaScript Hack That Makes Any Website Work Offline
Imagínese a alguien navegando por su sitio mientras viaja en tren, con la señal cayendo y volviendo a establecerse cada pocos segundos. En la mayoría de los sitios web, esa conexión intermitente significa un callejón sin salida: un error del navegador que indica que están desconectados. ¿Qué pasaría si su sitio pudiera evitar completamente ese problema y seguir funcionando igualmente?
Esto no es alguna función experimental futura. Se puede lograr ahora mismo utilizando una tecnología del navegador llamada Service Worker. Un Service Worker funciona como un intermediario programable que se sitúa entre su aplicación web y la red. Puede capturar las solicitudes antes de que lleguen al servidor y responderlas por sí mismo utilizando archivos almacenados localmente, lo que permite que su sitio siga funcionando incluso sin conexión alguna.
Este truco no solo proporciona una solución alternativa útil, sino que también puede acelerar significativamente los tiempos de carga y hacer que su aplicación sea mucho más resistente a redes deficientes. A pesar de sonar avanzado, su configuración es bastante sencilla.
Qué hace posible este truco: Service Workers combinados con la Cache API
Dos características del navegador trabajan juntas para hacer esto posible:
- Service Worker: un script en segundo plano que se ejecuta de forma independiente del hilo principal de su página, funcionando como una capa de red alternativa.
- Cache API: una interfaz integrada del navegador que le permite almacenar y recuperar recursos de forma programática, similar a un almacén clave-valor. Aquí es donde guardará el HTML, CSS, JavaScript, imágenes y otros recursos de los que depende su sitio.
Aquí está el flujo general: la primera vez que alguien visita su sitio mientras está conectado a Internet, el Service Worker se instala y descarga los archivos importantes en su caché. A partir de ese momento, toda solicitud de red que realice la página pasa primero por el Service Worker. Cuando el visitante pierde la conexión, el worker responde utilizando lo que ya tiene almacenado en caché en lugar de intentar —y fallar— llegar al servidor, por lo que la página sigue cargándose casi al instante.
Paso 1: Escribir el script del Service Worker
Comience creando un archivo llamado sw.js en la raíz de su proyecto. Este es el script encargado de instalar el caché e interceptar las solicitudes futuras.
// sw.js
const CACHE_NAME = 'my-website-cache-v1';
const urlsToCache = [
'/',
'/index.html',
'/styles.css',
'/script.js',
'/images/logo.png'
];
// The 'install' event is fired when the service worker is first installed.
self.addEventListener('install', event => {
event.waitUntil(
caches.open(CACHE_NAME)
.then(cache => {
console.log('Opened cache');
return cache.addAll(urlsToCache);
})
);
});
// The 'fetch' event is fired for every network request.
self.addEventListener('fetch', event => {
event.respondWith(
caches.match(event.request)
.then(response => {
// If the request is in the cache, serve the cached version.
if (response) {
console.log('Serving from cache:', event.request.url);
return response;
}
// Otherwise, fetch from the network.
console.log('Fetching from network:', event.request.url);
return fetch(event.request);
})
);
});
Este archivo se encarga principalmente de escuchar dos eventos de ciclo de vida:
install: aquí es donde se abre un caché con nombre y se rellena con la lista de archivos que deseas tener disponibles incluso sin conexión a red.fetch: este evento se dispara para cada solicitud que realiza la página. El worker verifica si ya existe una respuesta correspondiente en el caché; si la hay, se devuelve esa copia almacenada, y de lo contrario, la solicitud se envía a la red como normal.
Paso 2: Registrar el Worker desde tu aplicación
Una vez que existe el script del worker, aún es necesario indicarle al navegador que lo utilice. Añade el siguiente fragmento a tu archivo de script principal, o incóldalo directamente dentro de una etiqueta <script> en tu HTML.
// Register the service worker
if ('serviceWorker' in navigator) {
window.addEventListener('load', () => {
navigator.serviceWorker.register('/sw.js')
.then(registration => {
console.log('Service Worker registered successfully:', registration);
})
.catch(error => {
console.error('Service Worker registration failed:', error);
});
});
}
Este bloque primero verifica si el navegador actual admite la API Service Worker, y si es así, registra su archivo sw.js. Observe la ruta utilizada para registrarlo: /sw.js debe encontrarse en la raíz de su sitio, ya que un Service Worker solo puede controlar las páginas que están dentro del ámbito del directorio desde el cual está registrado.
Verificación de su funcionamiento
Para observar esto en acción, cargue su sitio en un navegador que implemente Service Workers; Chrome, Firefox y Edge cumplen con este requisito.
- Abra las herramientas de desarrollo del navegador (F12 o Ctrl+Shift+I).
- Pase al panel Application.
- En la barra lateral, busque Service Workers. Su archivo
sw.jsdebería aparecer allí como activo.
Si todo está configurado correctamente, la página se cargará de inmediato a pesar de no tener acceso a la red. La salida de la consola en las herramientas de desarrollo confirmará que las solicitudes se están interceptando y respondiendo directamente desde el caché en lugar de desde la red.
Profundizando en el patrón: Estrategias alternativas de caché
Lo que acaba de describirse se conoce como un enfoque cache-first, network-fallback: es ideal para recursos que rara vez cambian, como imágenes estáticas o scripts empaquetados. Pero los Service Workers no se limitan a este único patrón; existen algunas estrategias comunes adicionales:
- Primero la red, luego el caché: adecuado para contenido que cambia con frecuencia y necesita mantenerse actualizado. El proceso intenta primero obtener la información desde la red, y solo recurre al contenido almacenado en caché si esa solicitud falla, por ejemplo debido a una mala conexión.
- Válido mientras se vuelve a validar: el proceso responde de inmediato con lo que ya está en caché para mayor rapidez, mientras simultáneamente envía una solicitud a la red en segundo plano para actualizar el caché la próxima vez que se cargue la página. Esto permite obtener respuestas rápidas de inmediato y contenido actualizado poco después.
Aprender este patrón básico para funcionamiento sin conexión también te prepara para crear aplicaciones web progresivas completas, donde el soporte sin conexión se combina con funcionalidades como notificaciones push y la posibilidad de instalar el sitio en la pantalla principal del dispositivo como una aplicación nativa.
Añadir este patrón a su proyecto representa una inversión relativamente pequeña que genera beneficios significativos: en lugar de que una conexión interrumpida arruine por completo la experiencia, sus usuarios apenas notarán la interrupción.
Lecturas relacionadas
- Dejar de sincronizar el estado con useEffect: un patrón más seguro en React — Aprenda por qué usar useEffect para sincronizar el estado derivado causa condiciones de carrera y renders adicionales, y cómo reemplazarlo con la derivación en tiempo de renderizado y el atributo key.
- Manejar estados de UI del mundo real con renderizado condicional en React — Aprenda cómo crear interfaces de autenticación, roles, permisos, carga, errores y estado vacío en React utilizando patrones prácticos de renderizado condicional.