tutoriales.com

Desarrollando PWA con Workbox: Cacheando Recursos de Forma Avanzada

Este tutorial te guiará paso a paso en el desarrollo de Progressive Web Apps (PWA) utilizando la potente librería Workbox. Aprenderás a implementar estrategias de caché para tus recursos, garantizando un rendimiento óptimo y una experiencia de usuario fluida incluso sin conexión a internet.

Intermedio20 min de lectura7 views16 de marzo de 2026Reportar error

Las Progressive Web Apps (PWA) están revolucionando la forma en que pensamos sobre las aplicaciones web, ofreciendo una experiencia similar a las aplicaciones nativas directamente desde el navegador. Uno de los pilares fundamentales de las PWA es su capacidad para funcionar offline, y esto se logra principalmente a través del uso de Service Workers y la gestión inteligente de la caché. En este tutorial, nos sumergiremos en el mundo de Workbox, una librería que simplifica enormemente la implementación de Service Workers y la gestión de estrategias de caché.

🚀 ¿Qué es Workbox y por qué usarlo?

Workbox es una colección de módulos de JavaScript que facilita el desarrollo de Service Workers. Si alguna vez has intentado escribir un Service Worker desde cero, sabrás que puede ser complejo manejar los eventos de instalación, activación, fetch, y coordinar las diferentes estrategias de caché. Workbox abstrae gran parte de esta complejidad, permitiéndote concentrarte en la lógica de tu aplicación. Te proporciona una API declarativa para definir cómo y cuándo deben cachearse tus recursos, así como cuándo deben ser recuperados de la red.

Beneficios clave de Workbox:

  • Simplicidad: Reduce la cantidad de código boilerplate que necesitas escribir.
  • Fiabilidad: Ofrece estrategias de caché probadas y optimizadas.
  • Flexibilidad: Permite configurar estrategias específicas para diferentes tipos de recursos.
  • Depuración: Incluye herramientas que facilitan la depuración de Service Workers.
  • Integración: Se integra fácilmente con herramientas de build como Webpack.
💡 Consejo: Aunque Workbox simplifica mucho, es crucial tener una comprensión básica de los Service Workers y el ciclo de vida de la caché para aprovecharlo al máximo.

🛠️ Configuración Inicial: Preparando tu Entorno

Antes de empezar a escribir código, necesitamos configurar un proyecto básico. Usaremos Node.js y un servidor web simple para simular un entorno de producción.

1. Inicializar un nuevo proyecto

Crea un nuevo directorio para tu proyecto y inicialízalo con npm:

mkdir my-pwa-workbox
cd my-pwa-workbox
npm init -y

2. Instalar dependencias

Necesitaremos Workbox y un servidor HTTP simple. http-server es una excelente opción para desarrollo.

npm install workbox-cli http-server --save-dev

3. Estructura básica de archivos

Crea los siguientes archivos en la raíz de tu proyecto:

  • index.html
  • app.js
  • style.css

index.html:

<!DOCTYPE html>
<html lang="es">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Mi PWA con Workbox</title>
    <link rel="stylesheet" href="style.css">
</head>
<body>
    <header>
        <h1>Hola PWA con Workbox</h1>
    </header>
    <main>
        <p>Esta es una aplicación web progresiva usando Workbox para el cacheo de recursos.</p>
        <img src="https://via.placeholder.com/150" alt="Placeholder Image">
        <button id="fetchData">Cargar Datos Externos</button>
        <div id="dataContainer"></div>
    </main>
    <script src="app.js"></script>
    <script>
        // Registrar el Service Worker
        if ('serviceWorker' in navigator) {
            window.addEventListener('load', () => {
                navigator.serviceWorker.register('/sw.js')
                    .then(registration => {
                        console.log('Service Worker registrado con éxito:', registration);
                    })
                    .catch(error => {
                        console.error('Fallo el registro del Service Worker:', error);
                    });
            });
        }
    </script>
</body>
</html>

app.js:

document.addEventListener('DOMContentLoaded', () => {
    console.log('App.js cargado');

    const fetchDataBtn = document.getElementById('fetchData');
    const dataContainer = document.getElementById('dataContainer');

    fetchDataBtn.addEventListener('click', async () => {
        dataContainer.innerHTML = 'Cargando datos...';
        try {
            const response = await fetch('https://jsonplaceholder.typicode.com/posts/1');
            const data = await response.json();
            dataContainer.innerHTML = `<h3>${data.title}</h3><p>${data.body}</p>`;
        } catch (error) {
            dataContainer.innerHTML = '<p style="color: red;">Error al cargar datos. ¿Estás offline?</p>';
            console.error('Error fetching data:', error);
        }
    });
});

style.css:

body {
    font-family: Arial, sans-serif;
    margin: 0;
    padding: 0;
    background-color: #f4f4f4;
    color: #333;
}

header {
    background-color: #007bff;
    color: white;
    padding: 1rem;
    text-align: center;
}

main {
    padding: 1rem;
    max-width: 800px;
    margin: 20px auto;
    background-color: white;
    box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
    border-radius: 8px;
}

img {
    max-width: 100%;
    height: auto;
    display: block;
    margin: 20px 0;
}

button {
    background-color: #28a745;
    color: white;
    border: none;
    padding: 10px 15px;
    border-radius: 5px;
    cursor: pointer;
    font-size: 1rem;
    margin-top: 15px;
}

button:hover {
    background-color: #218838;
}

#dataContainer {
    margin-top: 20px;
    border-top: 1px solid #eee;
    padding-top: 15px;
}

📝 Generando el Service Worker con Workbox CLI

Workbox CLI es la forma más sencilla de empezar. Nos permite generar un archivo Service Worker (sw.js) con una configuración básica para precachear nuestros activos estáticos.

1. Configurar Workbox CLI

Para generar un sw.js, Workbox CLI necesita saber qué archivos precachear y cómo configurarlos. Podemos hacer esto usando el comando wizard.

npx workbox wizard

Este comando te hará una serie de preguntas:

  • Where is the root of your web app? (¿Dónde está la raíz de tu aplicación web?): Escribe . (punto) ya que nuestros archivos están en la raíz.
  • Which file types would you like to cache? (¿Qué tipos de archivo quieres cachear?): Selecciona las opciones por defecto (js, css, html, png, jpg, gif, svg, webp).
  • What is the name of the Service Worker file? (¿Cuál es el nombre del archivo Service Worker?): Deja el valor por defecto sw.js.
  • Where would you like to save the configuration file? (¿Dónde quieres guardar el archivo de configuración?): Deja el valor por defecto workbox-config.js.

Después de esto, Workbox CLI generará dos archivos: sw.js (tu Service Worker) y workbox-config.js (el archivo de configuración para futuras regeneraciones).

workbox-config.js (Ejemplo de lo que se genera):

module.exports = {
	"globDirectory": ".",
	"globPatterns": [
		"**/*.{js,css,html,png,jpg,gif,svg,webp}"
	],
	"swDest": "./sw.js",
	"swSrc": "./sw.js"
};

Este archivo define qué archivos buscar (globPatterns) y dónde generar el Service Worker (swDest). La propiedad swSrc se usa si ya tienes un Service Worker y quieres que Workbox lo "inyecte" con la lista de precache. Por ahora, el sw.js generado es suficiente.

2. Entendiendo el sw.js generado

Abre sw.js. Verás algo similar a esto (el contenido exacto puede variar ligeramente):

importScripts('https://storage.googleapis.com/workbox-cdn/releases/6.1.5/workbox-sw.js');

if (workbox) {
    console.log(`Yay! Workbox está cargado 🎉`);

    workbox.precaching.precacheAndRoute(self.__WB_MANIFEST);

} else {
    console.log(`Boo! Workbox no pudo ser cargado 😬`);
}

La línea importScripts(...) carga la librería Workbox. La magia ocurre con workbox.precaching.precacheAndRoute(self.__WB_MANIFEST);. self.__WB_MANIFEST es un array que Workbox CLI inyecta automáticamente con la lista de archivos que deben ser precacheados, basados en globPatterns de tu workbox-config.js. Cuando el Service Worker se instala, estos archivos se descargan y se guardan en caché, haciendo que tu aplicación funcione offline instantáneamente para esos recursos.

🔥 Importante: Cada vez que realices cambios en tus activos estáticos (HTML, CSS, JS, imágenes), deberás ejecutar `npx workbox generateSW workbox-config.js` para regenerar `sw.js` y actualizar el manifiesto de precache.

🌐 Estrategias de Caché con Workbox: Más Allá del Precache

El precaching es excelente para los activos estáticos principales de tu aplicación, pero ¿qué pasa con los recursos que se solicitan en tiempo de ejecución, como imágenes de un CDN o datos de una API? Aquí es donde entran en juego las estrategias de caché de Workbox.

Modificaremos nuestro sw.js para incluir estas estrategias. Después de la línea workbox.precaching.precacheAndRoute(self.__WB_MANIFEST);, añade las siguientes configuraciones.

1. Cacheando imágenes: CacheFirst

Para imágenes, a menudo queremos usar una estrategia CacheFirst. Esto significa que el Service Worker intentará servir la imagen desde la caché primero. Si no está en caché, la solicitará a la red y luego la guardará en caché para futuras solicitudes. Es ideal para imágenes que no cambian con frecuencia.

// sw.js (añade esto después del precache)

// Cachear imágenes con CacheFirst
workbox.routing.registerRoute(
    /.*\.(?:png|gif|jpg|jpeg|svg|webp)$/,
    new workbox.strategies.CacheFirst({
        cacheName: 'images-cache',
        plugins: [
            new workbox.expiration.ExpirationPlugin({
                maxEntries: 50, // Limitar a 50 imágenes
                maxAgeSeconds: 30 * 24 * 60 * 60, // 30 días
            }),
            new workbox.cacheableResponse.CacheableResponsePlugin({
                statuses: [0, 200], // Cachear solo respuestas válidas
            }),
        ],
    })
);
  • workbox.routing.registerRoute(): Define una regla de enrutamiento. El primer argumento es una expresión regular para URLs que coinciden con la regla.
  • new workbox.strategies.CacheFirst(): Crea una nueva instancia de la estrategia CacheFirst.
  • cacheName: Un nombre para la caché específica (útil para la depuración).
  • plugins: Array de plugins de Workbox para extender la funcionalidad.
    • ExpirationPlugin: Gestiona la caducidad de elementos en la caché (número máximo de entradas y tiempo de vida).
    • CacheableResponsePlugin: Asegura que solo se cacheen respuestas con ciertos códigos de estado (0 para opacos, 200 para éxitos).
📌 Nota: La imagen de placeholder en `index.html` es de `https://via.placeholder.com`. La expresión regular `/.*\.(?:png|gif|jpg|jpeg|svg|webp)$/` la capturará y cacheará.

2. Cacheando APIs: NetworkFirst

Para datos de API, a menudo necesitamos la información más reciente, pero queremos tener una opción de respaldo si la red no está disponible. NetworkFirst es perfecta para esto: intenta obtener la respuesta de la red. Si tiene éxito, la usa y la actualiza en la caché. Si la red falla, recurre a la versión cacheada.

// sw.js (continúa añadiendo)

// Cachear APIs con NetworkFirst
workbox.routing.registerRoute(
    /^https:\/\/jsonplaceholder\.typicode\.com\/posts\//,
    new workbox.strategies.NetworkFirst({
        cacheName: 'api-cache',
        plugins: [
            new workbox.expiration.ExpirationPlugin({
                maxEntries: 10, // Limitar a 10 entradas de API
                maxAgeSeconds: 24 * 60 * 60, // 24 horas
            }),
            new workbox.cacheableResponse.CacheableResponsePlugin({
                statuses: [0, 200],
            }),
        ],
    })
);

Aquí estamos apuntando a la API de jsonplaceholder que usamos en app.js.

3. Cacheando CSS y JS de terceros: StaleWhileRevalidate

Para recursos como librerías CSS o JavaScript de CDNs (por ejemplo, Google Fonts, Bootstrap), StaleWhileRevalidate es una excelente elección. Sirve el recurso inmediatamente desde la caché (si está disponible) mientras que, en segundo plano, solicita una versión actualizada de la red. La próxima vez que se solicite, se servirá la versión más reciente.

// sw.js (continúa añadiendo)

// Cachear estilos y scripts de terceros con StaleWhileRevalidate
workbox.routing.registerRoute(
    /.*(?:googleapis|gstatic)\.com.*$/,
    new workbox.strategies.StaleWhileRevalidate({
        cacheName: 'third-party-cache',
        plugins: [
            new workbox.expiration.ExpirationPlugin({
                maxEntries: 20,
                maxAgeSeconds: 7 * 24 * 60 * 60, // 7 días
            }),
            new workbox.cacheableResponse.CacheableResponsePlugin({
                statuses: [0, 200],
            }),
        ],
    })
);

Esta regla es general para recursos de Google, pero puedes adaptarla a cualquier CDN que uses.


🧪 Probando tu PWA y Workbox

Una vez que hayas modificado sw.js con las nuevas estrategias, es crucial probar tu aplicación para asegurarte de que todo funciona como se espera.

1. Regenerar el Service Worker

Recuerda ejecutar este comando para que los cambios en workbox-config.js y las nuevas estrategias se incluyan en sw.js:

npx workbox generateSW workbox-config.js

2. Iniciar el servidor web

npx http-server .

Abre tu navegador (preferiblemente Chrome) en la URL que te proporciona http-server (normalmente http://localhost:8080).

3. Usando las herramientas de desarrollo de Chrome

  1. Abre las Herramientas de Desarrollo (F12 o Ctrl + Shift + I).
  2. Ve a la pestaña Application.
  3. En el menú de la izquierda, selecciona Service Workers.
    • Deberías ver tu sw.js listado como activo y corriendo. Si hay versiones anteriores, asegúrate de marcar Update on reload y recargar la página.
    • Haz clic en el ID del Service Worker para abrirlo en una nueva pestaña y examinar su código.
  4. En el menú de la izquierda, selecciona Cache Storage.
    • Aquí verás las cachés creadas por Workbox: workbox-precache-v2, images-cache, api-cache, third-party-cache.
    • Explora el contenido de cada caché. Verás index.html, app.js, style.css en la caché de precache. Cuando visites la página, la imagen de placeholder aparecerá en images-cache. Cuando hagas clic en el botón, la respuesta de la API aparecerá en api-cache.
  5. Ve a la pestaña Network.
    • Recarga la página. Observa la columna Size o Type. Para los recursos precacheados, deberías ver (from ServiceWorker).
    • Haz clic en el botón Cargar Datos Externos. Observa cómo se carga la API. Recarga la página y vuelve a hacer clic; si la red es inestable, verás cómo Workbox gestiona la respuesta.
  6. Simular offline: En la pestaña Network, marca la casilla Offline. Recarga tu aplicación.
    • Deberías ver que index.html, app.js, style.css y la imagen se cargan perfectamente desde la caché.
    • Intenta hacer clic en Cargar Datos Externos. Deberías obtener la versión cacheada si la estrategia es NetworkFirst y ya se había cacheado previamente, o un mensaje de error si no hay caché y estás offline.
Registro SW Instalación (Precache) Activación Sincronización de Fondo Fetch (Estrategias) Cache First Network First Stale While Revalidate Respuesta

Tabla de Estrategias de Caché Comunes

EstrategiaDescripciónCasos de Uso ComunesVentajasDesventajas
CacheFirstIntenta obtener de la caché; si no está, va a la red y cachea la respuesta.Imágenes estáticas, activos de aplicación poco cambiantes.Muy rápido offline, reduce carga del servidor.Puede servir contenido obsoleto.
NetworkFirstIntenta obtener de la red; si falla, va a la caché. La respuesta de red siempre actualiza la caché.Datos de API importantes, contenido dinámico que requiere frescura.Siempre intenta obtener lo más reciente; tiene fallback offline.Lento en redes lentas si la caché no se usa hasta que la red falla.
StaleWhileRevalidateSirve de la caché inmediatamente y, en paralelo, solicita de la red para actualizar la caché.CSS/JS de terceros, feeds de noticias, avatars de usuario.Rápido para el usuario; la caché se mantiene actualizada en segundo plano.La primera solicitud después de una actualización de caché puede ser obsoleta.
NetworkOnlySolo va a la red. No usa la caché.Solicitudes que siempre requieren la información más reciente (ej. autenticación).Siempre obtiene los datos más frescos.No funciona offline; no hay fallback.
CacheOnlySolo va a la caché. No va a la red.Activos precacheados, recursos que nunca cambian.Extremadamente rápido; funciona totalmente offline.No puede obtener recursos nuevos o actualizados.

✨ Consideraciones Adicionales y Mejores Prácticas

Offline Fallback Page

Es una buena práctica proporcionar una página de fallback cuando el usuario está offline y el recurso solicitado no está en caché. Workbox facilita esto.

  1. Crea un archivo offline.html en tu raíz:
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Offline</title>
<style>
body { font-family: Arial, sans-serif; text-align: center; padding-top: 50px; }
h1 { color: #555; }
p { color: #777; }
</style>
</head>
<body>
<h1>Estás offline 😞</h1>
<p>Parece que no tienes conexión a internet y el contenido que buscas no está disponible offline.</p>
<p>Por favor, revisa tu conexión y vuelve a intentarlo.</p>
</body>
</html>
  1. Asegúrate de que offline.html esté precacheado. Edita workbox-config.js y añade offline.html a globPatterns si no está ya, o simplemente vuelve a ejecutar el wizard y asegúrate de seleccionarlo. Luego, regenera el SW: npx workbox generateSW workbox-config.js.

  2. Añade la siguiente configuración a tu sw.js (después de las estrategias de caché):

// sw.js (al final del archivo, antes del `}` del `if (workbox)`) 

// Página de fallback offline
workbox.routing.setCatchHandler(async ({event}) => {
if (event.request.mode === 'navigate') {
return caches.match('/offline.html');
}
return Response.error(); // Para otros tipos de peticiones, simplemente falla
});
Ahora, si un usuario intenta navegar a una URL que no está en caché mientras está offline, verá `offline.html` en lugar de la página de error del navegador.

Push Notifications y Background Sync

Workbox también tiene módulos para Push Notifications (workbox-notifications) y Background Sync (workbox-background-sync), lo que te permite enviar notificaciones a los usuarios incluso cuando la aplicación no está abierta y sincronizar datos en segundo plano cuando la conectividad se restablece. Estos son temas más avanzados, pero Workbox simplifica su implementación significativamente.

Actualizaciones del Service Worker

Cuando realizas cambios en tu sw.js o en los activos precacheados y regeneras el Service Worker, los navegadores detectan que hay una nueva versión. El nuevo Service Worker se instala en segundo plano y se activa cuando todas las pestañas que usan el Service Worker anterior se cierran o se recargan. Workbox ofrece una forma de forzar la activación inmediatamente si es necesario, pero generalmente es mejor dejar que el navegador maneje esto para evitar interrupciones en la experiencia del usuario.

¿Qué es la estrategia `CacheFirst` y cuándo debo usarla? `CacheFirst` es una estrategia de red que intenta satisfacer una solicitud primero desde la caché. Si el recurso se encuentra en la caché, se devuelve inmediatamente. Si no se encuentra, la solicitud se envía a la red, y la respuesta se cachea antes de ser devuelta. Es ideal para recursos estáticos que cambian muy poco o nada, como imágenes de cabecera, iconos de aplicaciones o archivos JavaScript y CSS que forman parte del esqueleto de la aplicación. Su principal ventaja es la velocidad, ya que la respuesta se obtiene del disco local, haciendo que la experiencia sea extremadamente rápida, especialmente en condiciones de red deficientes o sin conexión.
¿Cómo puedo depurar mi Service Worker y las cachés de Workbox? Las Herramientas de Desarrollo de Chrome (y otros navegadores) son tus mejores aliados. La pestaña `Application` > `Service Workers` te permite ver el estado de tu SW, forzar actualizaciones y anular registros. La sección `Cache Storage` dentro de `Application` te muestra todas las cachés gestionadas por tu SW y su contenido. Además, la pestaña `Network` es fundamental: puedes ver de dónde viene cada recurso (red o Service Worker) y simular el modo offline para probar tu estrategia de caché. Workbox también tiene un modo de depuración que puedes habilitar para ver mensajes más detallados en la consola.
90% Completado

Este tutorial te ha proporcionado una base sólida para construir PWAs robustas y fiables con Workbox. Hemos cubierto desde la configuración inicial hasta la implementación de estrategias de caché avanzadas para diferentes tipos de recursos. Con estos conocimientos, estás bien equipado para crear experiencias web que deleiten a tus usuarios, estén online u offline.

Comentarios (0)

Aún no hay comentarios. ¡Sé el primero!