Una herramienta de productividad cotidiana —como un temporizador de estudio, un gestor de tareas o una calculadora de presupuestos— no puede dejar de funcionar porque el usuario subió a un avión, entró a una cafetería con Wi-Fi inestable o perdió temporalmente la cobertura de datos móviles. La arquitectura Offline-First mediante Progressive Web Apps (PWA) convierte una página web tradicional en una aplicación instalable y autónoma que carga instantáneamente desde el almacenamiento local del dispositivo.
1. El Ciclo de Vida de un Service Worker
Un Service Worker es un hilo de trabajo en segundo plano (Web Worker especializado) que actúa como un proxy de red programable entre la aplicación web, el almacenamiento en caché del navegador (CacheStorage) y el servidor remoto. Su ciclo de vida consta de tres fases críticas:
- Install (Instalación): Momento ideal para pre-cachear el App Shell (los archivos esenciales:
index.html, hoja de estilos, lógica JavaScript e íconos vectoriales). - Activate (Activación): Fase donde se purgan las cachés de versiones anteriores de la aplicación para liberar espacio en disco y evitar conflictos de esquemas.
- Fetch (Intercepción): Cada solicitud de red originada por la página pasa por el evento
fetch, permitiendo decidir algorítmicamente cómo responder.
2. Selección de la Estrategia de Caché Correcta según el Recurso
Aplicar una única política de caché para todos los recursos de una aplicación es un error común que lleva a mostrar versiones obsoletas o a romper llamadas de API dinámicas. La matriz óptima de ingeniería divide los activos en tres categorías:
const CACHE_VERSION = 'pomotask-v2.4.0';
const STATIC_ASSETS = [
'/',
'/index.html',
'/style.css',
'/app.js',
'/audio.js',
'/manifest.json'
];
self.addEventListener('install', (event) => {
event.waitUntil(
caches.open(CACHE_VERSION).then((cache) => cache.addAll(STATIC_ASSETS))
);
self.skipWaiting();
});
self.addEventListener('activate', (event) => {
event.waitUntil(
caches.keys().then((keys) =>
Promise.all(keys.filter((k) => k !== CACHE_VERSION).map((k) => caches.delete(k)))
)
);
self.clients.claim();
});
A. Stale-While-Revalidate para el App Shell (HTML, CSS, JS)
Con la estrategia Stale-While-Revalidate, cuando el usuario abre la aplicación, el Service Worker entrega inmediatamente la versión almacenada en CacheStorage (logrando un tiempo de carga de 5 a 15 ms) y, en paralelo, realiza una petición en segundo plano al servidor para actualizar la caché con la versión más reciente disponible para la próxima apertura.
B. Network-First con Fallback para APIs de Sincronización
Para rutas de datos dinámicos (como /api/tasks o /api/stats), intentamos primero consultar el servidor con un tiempo límite (timeout) corto de 3 segundos; si el dispositivo está sin conexión, devolvemos la última instantánea válida o encolamos las mutaciones en localStorage / IndexedDB para sincronizarlas cuando se dispare el evento window.addEventListener('online', ...).
3. El Manifiesto Web (manifest.json) y Ergonomía en iOS y Android
Para que el sistema operativo instale la PWA en la pantalla de inicio con su propio ícono y sin la barra de direcciones del navegador, el archivo manifest.json debe declarar "display": "standalone" junto con íconos en formato estándar y "purpose": "maskable" (para que Android recorte el ícono de forma adaptativa sin bordes blancos). Asimismo, incluir las etiquetas apple-mobile-web-app-capable y manejar las áreas seguras de pantalla (env(safe-area-inset-bottom)) garantiza que los menús inferiores no se solapen con la barra gestual de iPhone y Android modernos.