Gestionando la Duración del Caché en PWAs: Estrategias de Expiración con Workbox
Este tutorial te guiará a través de la implementación de estrategias de expiración de caché en Progressive Web Apps (PWAs) utilizando Workbox. Aprenderás a configurar reglas para la duración y el tamaño del caché, asegurando que tus usuarios siempre tengan contenido actualizado y un rendimiento óptimo.
Las Progressive Web Apps (PWAs) han revolucionado la forma en que pensamos sobre las aplicaciones web, ofreciendo experiencias de usuario rápidas, fiables y atractivas, incluso sin conexión. Uno de los pilares fundamentales para lograr esto es el Service Worker y la gestión eficiente del caché. Sin una estrategia de caché adecuada, una PWA puede terminar sirviendo contenido desactualizado o, peor aún, consumiendo demasiado espacio de almacenamiento en el dispositivo del usuario.
Aquí es donde Workbox entra en juego. Workbox es una biblioteca de módulos JavaScript que facilita la escritura, gestión y depuración de Service Workers. Ofrece una abstracción poderosa sobre las APIs del Service Worker, simplificando tareas complejas como el cacheo de recursos y la gestión de estrategias de red.
En este tutorial, nos centraremos en cómo gestionar la duración del caché utilizando las capacidades de expiración que Workbox nos proporciona. Esto es crucial para mantener un equilibrio entre el rendimiento offline y la frescura del contenido.
🚀 ¿Por qué es Crucial la Expiración del Caché?
Imagina que tienes una PWA de noticias. Si cacheas las noticias indefinidamente, tus usuarios verán información obsoleta. Por otro lado, si no cacheas nada, perderán la experiencia offline. La expiración del caché nos permite definir cuándo un recurso en caché debe considerarse "viejo" y ser reemplazado o eliminado.
Beneficios Clave:
- Frescura del Contenido: Asegura que los usuarios vean la versión más reciente de tus activos.
- Optimización del Almacenamiento: Evita que el caché crezca indefinidamente, consumiendo espacio valioso en el dispositivo del usuario.
- Control Fino: Permite aplicar diferentes políticas a distintos tipos de recursos (imágenes, CSS, JS, APIs).
- Rendimiento Equilibrado: Mantiene la velocidad de carga para activos estáticos mientras se asegura la actualización de datos dinámicos.
🛠️ Configuración Inicial: Preparando Workbox
Antes de sumergirnos en las estrategias de expiración, necesitamos una configuración básica de Workbox en nuestro proyecto. Asumiremos que ya tienes una PWA o una aplicación web que quieres convertir en PWA.
1. Instalar Workbox
Si estás usando un bundler como Webpack, puedes instalar Workbox a través de npm o yarn.
npm install workbox-webpack-plugin --save-dev
# o
yarn add workbox-webpack-plugin --dev
Si prefieres usar Workbox directamente en tu Service Worker sin un bundler (por ejemplo, vía CDN o módulos ES), puedes omitir esto y cargar Workbox en tu sw.js.
2. Registrar el Service Worker
Tu index.html (o el archivo principal de tu aplicación) debe registrar el Service Worker.
// public/index.js (o similar)
if ('serviceWorker' in navigator) {
window.addEventListener('load', () => {
navigator.serviceWorker.register('/sw.js')
.then(registration => {
console.log('Service Worker registrado con éxito:', registration.scope);
})
.catch(error => {
console.error('Fallo en el registro del Service Worker:', error);
});
});
}
3. Crear el Archivo del Service Worker (sw.js)
Este es el corazón de nuestro control de caché. Aquí importaremos Workbox y definiremos nuestras reglas.
// public/sw.js
importScripts('https://storage.googleapis.com/workbox-cdn/releases/6.4.1/workbox-sw.js');
if (workbox) {
console.log('Workbox está cargado 🎉');
// Precaching de recursos estáticos
// Aquí, Workbox precacheará los archivos listados en el manifest generado por workbox-webpack-plugin
workbox.precaching.precacheAndRoute(self.__WB_MANIFEST);
} else {
console.log('Workbox no ha podido cargarse 😬');
}
⏳ Estrategias de Expiración de Caché con Workbox
Workbox ofrece el módulo workbox-expiration para gestionar la expiración del caché. Este módulo se integra perfectamente con las estrategias de cacheo de Workbox (como StaleWhileRevalidate, CacheFirst, NetworkFirst, etc.).
Las dos opciones principales para la expiración son:
maxEntries: Limita el número de entradas (respuestas) en el caché.maxAgeSeconds: Define la duración máxima en segundos que una entrada puede permanecer en el caché.
Veamos cómo aplicarlas.
1. Expiración Basada en el Tiempo (maxAgeSeconds) ⏰
Esta estrategia es ideal para recursos cuyo contenido cambia con cierta frecuencia y no quieres que los usuarios vean versiones muy antiguas. Por ejemplo, imágenes de perfil, datos de API, o CSS/JS que no están precacheados.
// public/sw.js
importScripts('https://storage.googleapis.com/workbox-cdn/releases/6.4.1/workbox-sw.js');
if (workbox) {
// ... (precaching si aplica)
// Ejemplo 1: Cachear imágenes con una duración de 30 días
workbox.routing.registerRoute(
/.*\.(?:png|jpg|jpeg|svg|gif)$/,
new workbox.strategies.CacheFirst({
cacheName: 'images-cache',
plugins: [
new workbox.expiration.Plugin({
maxAgeSeconds: 30 * 24 * 60 * 60, // 30 días
})
]
})
);
// Ejemplo 2: Cachear respuestas de API con una duración de 1 hora
workbox.routing.registerRoute(
/^https:\/\/api\.example\.com\/data/,
new workbox.strategies.NetworkFirst({
cacheName: 'api-data-cache',
plugins: [
new workbox.expiration.Plugin({
maxAgeSeconds: 60 * 60, // 1 hora
purgeOnQuotaError: true, // Borrar automáticamente si el navegador se queda sin espacio
})
]
})
);
} else {
console.log('Workbox no ha podido cargarse 😬');
}
2. Expiración Basada en el Número de Entradas (maxEntries) 📊
Esta estrategia es útil para evitar que el caché crezca indefinidamente con un gran número de recursos. Es común para el cacheo de avatares, miniaturas o páginas visitadas recientemente, donde solo queremos mantener un número limitado de los más recientes.
// public/sw.js
importScripts('https://storage.googleapis.com/workbox-cdn/releases/6.4.1/workbox-sw.js');
if (workbox) {
// ... (precaching si aplica)
// Ejemplo 3: Cachear hasta 50 páginas HTML visitadas recientemente
workbox.routing.registerRoute(
/.*\.html$/,
new workbox.strategies.NetworkFirst({
cacheName: 'html-pages-cache',
plugins: [
new workbox.expiration.Plugin({
maxEntries: 50, // Mantener un máximo de 50 páginas HTML
})
]
})
);
// Ejemplo 4: Combinar maxEntries y maxAgeSeconds para assets genéricos
workbox.routing.registerRoute(
/.*\.(?:js|css)$/,
new workbox.strategies.StaleWhileRevalidate({
cacheName: 'assets-cache',
plugins: [
new workbox.expiration.Plugin({
maxEntries: 60, // Máximo 60 archivos JS/CSS
maxAgeSeconds: 7 * 24 * 60 * 60, // Válido por 7 días
})
]
})
);
} else {
console.log('Workbox no ha podido cargarse 😬');
}
🧠 Estrategias de Cacheo y Expiración Combinadas
La verdadera potencia de Workbox se revela al combinar estrategias de cacheo con plugins de expiración. Cada estrategia (CacheFirst, NetworkFirst, StaleWhileRevalidate, NetworkOnly, CacheOnly) tiene un comportamiento diferente en cuanto a cómo interactúa con la red y el caché. Al añadir la expiración, refinamos ese comportamiento.
Tabla de Combinaciones Comunes:
| Estrategia de Workbox | maxAgeSeconds | maxEntries | Caso de Uso Típico |
|---|---|---|---|
| --- | --- | --- | --- |
CacheFirst | ✅ Sí | ✅ Sí | Imágenes estáticas, fuentes (siempre servir del caché si está disponible y no expirado) |
NetworkFirst | ✅ Sí | ✅ Sí | APIs dinámicas, HTML (intentar red primero, caer a caché si falla, pero el caché puede expirar) |
| --- | --- | --- | --- |
StaleWhileRevalidate | ✅ Sí | ✅ Sí | CSS, JS, recursos que pueden ser "ligeramente" antiguos, pero queremos actualizar en segundo plano |
CacheOnly | ❌ N/A (solo precaching) | ❌ N/A | Recursos precacheados que no cambian (no necesitan expiración explícita) |
| --- | --- | --- | --- |
NetworkOnly | ❌ N/A | ❌ N/A | Solicitudes que siempre deben ir a la red (no se cachean) |
Diagrama de Flujo: CacheFirst con maxAgeSeconds
En el diagrama anterior, la expiración actúa como una puerta adicional antes de servir desde el caché, asegurando que solo se sirvan recursos frescos.
Diagrama de Flujo: StaleWhileRevalidate con maxEntries
Aquí, el maxEntries se aplica después de que una nueva respuesta se guarda en caché, manteniendo el tamaño del caché bajo control.
📈 Uso de purgeOnQuotaError
Cuando el navegador se queda sin espacio de almacenamiento para el Service Worker (cuota de almacenamiento), Workbox puede intentar purgar automáticamente los cachés marcados con purgeOnQuotaError: true. Esta es una medida de contingencia útil para evitar que tu PWA sea desinstalada por el navegador debido al exceso de uso de almacenamiento.
// public/sw.js
// ... (otras rutas)
workbox.routing.registerRoute(
/.*\.(?:webp|avif)$/,
new workbox.strategies.CacheFirst({
cacheName: 'high-res-images-cache',
plugins: [
new workbox.expiration.Plugin({
maxAgeSeconds: 60 * 60 * 24 * 7, // 7 días
maxEntries: 100,
purgeOnQuotaError: true, // Activar purga si hay error de cuota
})
]
})
);
✨ Buenas Prácticas y Consideraciones Avanzadas
Al implementar la expiración de caché, ten en cuenta las siguientes buenas prácticas:
-
Audita tu Caché: Utiliza las Developer Tools del navegador (pestaña "Application" -> "Cache Storage" y "IndexedDB") para monitorizar el tamaño y contenido de tus cachés.
-
Diferencia Cachés: Usa
cacheNamedistintos para diferentes tipos de recursos. Esto te da un control más granular sobre las políticas de expiración y facilita la depuración. -
Versiona tus URLs: Para recursos estáticos (CSS, JS, imágenes críticas) que cambian con cada despliegue, considera versionar sus URLs (ej.
app.12345.js). Esto asegura que el Service Worker precachee la nueva versión y el navegador no sirva una versión antigua desde el caché HTTP o el caché del Service Worker si no has configurado la expiración. -
Usa
backgroundSyncpara Revalidación: Para datos críticos de API, considera usar la API de Sincronización en Segundo Plano de Workbox si necesitas revalidar datos específicos incluso cuando el usuario está offline o ha cerrado la aplicación. Esto va más allá de la expiración de caché, pero es un complemento útil. -
Prueba exhaustivamente: Simula diferentes escenarios de red (offline, baja conectividad) y ciclos de vida del Service Worker (actualización, reinstalación) para asegurarte de que tus estrategias de expiración funcionan como esperas.
¿Cómo depurar problemas de caché?
Usa la pestaña Application en las Developer Tools de Chrome/Firefox:
- Service Workers: Desregistra, actualiza y detén tu Service Worker.
- Cache Storage: Revisa el contenido de tus cachés, elimina entradas o borra cachés completos.
- Clear Storage: Borra todos los datos del sitio, incluyendo Service Workers y cachés, para un inicio limpio.
Además, puedes añadir console.log en tu sw.js para ver el flujo de ejecución y los mensajes de Workbox.
Ejemplo de Estrategia Completa para una PWA Típica:
// public/sw.js - Estrategia de cacheo completa
importScripts('https://storage.googleapis.com/workbox-cdn/releases/6.4.1/workbox-sw.js');
if (workbox) {
console.log('Workbox está cargado y listo para cachear 🎉');
// 1. Precaching de activos generados por el build (HTML, CSS, JS versionados)
workbox.precaching.precacheAndRoute(self.__WB_MANIFEST);
// 2. Cacheo de fuentes de Google Fonts (CacheFirst con expiración larga)
workbox.routing.registerRoute(
/^https:\/\/fonts\.(?:googleapis|gstatic)\.com\//,
new workbox.strategies.CacheFirst({
cacheName: 'google-fonts',
plugins: [
new workbox.expiration.Plugin({
maxEntries: 30,
maxAgeSeconds: 365 * 24 * 60 * 60, // 1 año
purgeOnQuotaError: true,
}),
],
})
);
// 3. Cacheo de imágenes (CacheFirst con expiración moderada)
workbox.routing.registerRoute(
/.*\.(?:png|gif|jpg|jpeg|svg|webp)$/,
new workbox.strategies.CacheFirst({
cacheName: 'images-cache',
plugins: [
new workbox.expiration.Plugin({
maxEntries: 60,
maxAgeSeconds: 30 * 24 * 60 * 60, // 30 días
purgeOnQuotaError: true,
}),
],
})
);
// 4. Cacheo de APIs (NetworkFirst con expiración corta, para datos dinámicos)
workbox.routing.registerRoute(
/^https:\/\/api\.tudominio\.com\/data/,
new workbox.strategies.NetworkFirst({
cacheName: 'api-data',
plugins: [
new workbox.expiration.Plugin({
maxEntries: 20,
maxAgeSeconds: 60 * 30, // 30 minutos
purgeOnQuotaError: true,
}),
],
})
);
// 5. Cacheo de otros activos (StaleWhileRevalidate con expiración estándar)
workbox.routing.registerRoute(
({ request }) => request.destination === 'script' || request.destination === 'style',
new workbox.strategies.StaleWhileRevalidate({
cacheName: 'static-assets',
plugins: [
new workbox.expiration.Plugin({
maxEntries: 50,
maxAgeSeconds: 7 * 24 * 60 * 60, // 7 días
purgeOnQuotaError: true,
}),
],
})
);
// 6. Cacheo de páginas HTML (NetworkFirst con expiración corta)
workbox.routing.registerRoute(
({ request }) => request.mode === 'navigate',
new workbox.strategies.NetworkFirst({
cacheName: 'html-pages',
plugins: [
new workbox.expiration.Plugin({
maxEntries: 20,
maxAgeSeconds: 60 * 60 * 2, // 2 horas
purgeOnQuotaError: true,
}),
],
})
);
// Habilitar navegación offline para páginas que no están precacheadas o fallan la red
workbox.routing.setDefaultHandler(
new workbox.strategies.NetworkOnly()
);
} else {
console.log('Workbox no ha podido cargarse 😬. Asegúrate de que la URL del CDN es correcta.');
}
Este ejemplo demuestra cómo puedes combinar diferentes estrategias y reglas de expiración para gestionar eficazmente los diversos tipos de recursos en tu PWA, optimizando tanto el rendimiento como la frescura del contenido. La clave es entender la naturaleza de cada recurso y cómo esperas que se comporte en escenarios online y offline.
✅ Conclusión
La gestión de la expiración del caché es un aspecto fundamental para construir PWAs robustas y eficientes. Workbox simplifica enormemente esta tarea, proporcionando herramientas poderosas y flexibles para definir cuándo y cómo deben purgarse los recursos en caché.
Al aplicar maxAgeSeconds y maxEntries de manera inteligente a tus diferentes tipos de recursos, puedes asegurar que tus usuarios disfruten de una experiencia rápida y actualizada, manteniendo al mismo tiempo el consumo de almacenamiento bajo control. Recuerda siempre probar tus estrategias a fondo y ajustarlas según las necesidades específicas de tu aplicación.
¡Ahora estás listo para llevar el control del caché de tu PWA al siguiente nivel! 🚀
Tutoriales relacionados
- Estrategias de Sincronización en PWA: Asegurando Datos Offline Consistentesintermediate18 min
- Notificaciones Push en PWA: Re-engagement y Experiencia de Usuario Mejoradaintermediate15 min
- Aprovechando la API de Sensores en PWAs: Realidad Aumentada y Experiencias Inmersivasintermediate18 min
- Aprovechando la API de Sincronización en Segundo Plano en PWAs: Datos Siempre al Díaintermediate18 min
- Explorando la API de Web Bluetooth en PWAs: Conectando Tu Web con el Mundo Físicointermediate18 min
Comentarios (0)
Aún no hay comentarios. ¡Sé el primero!