Comprender y usar los trabajadores de servicio en JavaScript para capacidades desactivadas

Los usuarios modernos de la web esperan experiencias instantáneas, incluso cuando las condiciones de red son pobres o inexistentes. Los trabajadores de servicios son la piedra angular de esta expectativa, permitiendo que las aplicaciones Web progresivas (PWAs) funcionen fuera de línea, cargan rápidamente en conexiones abatidas y proporcionan notificaciones de presión. A diferencia de las técnicas tradicionales de caché, un trabajador de servicio actúa como un proxy programable sentado entre el navegador y la red.

Los trabajadores de servicio no son una nueva tecnología — han sido apoyados en los principales navegadores durante varios años— pero siguen siendo subutilizados por muchos equipos de desarrollo. Cuando se construyen correctamente, transforman una aplicación estática en una experiencia resiliente, similar a la aplicación. Vamos a caminar a través del ciclo completo de vida, discutir estrategias de caché que se adapten a diferentes tipos de contenido, y cubrir APIs de compañeros como Sinc de fondo y Push Notificaciones.

¿Qué es un trabajador de servicio?

Un Trabajador de Servicio es un archivo JavaScript que se ejecuta en un alcance global separado de su página web, en su propio hilo. No tiene acceso DOM y se comunica con su página solamente a través . Su trabajo principal es actuar como intermediario para las solicitudes de red: el navegador puede enviar cualquier solicitud de su página web al Trabajador de Servicio, que luego decide cómo responder —desde la red, desde una respuesta de red, o mediante la construcción de una respuesta personalizada.

Debido a que funciona en el fondo, un trabajador de servicio puede seguir operando incluso después de que el usuario cierre la página que lo registró. Esto lo hace ideal para tareas como:

  • Apoyo a la oficina] – servir páginas en caché cuando la red no esté disponible.
  • Sincronización de datos de fondo – realizar acciones mientras se encuentran fuera de línea y enviarlas cuando la conectividad regrese.
  • Notificaciones de archivo] – recibir mensajes de un servidor y mostrarlos al usuario incluso cuando la aplicación no esté abierta.
  • Optimización de rendimiento] – activos críticos pre-caching para que la aplicación se cargue instantáneamente en visitas de repetición.

Los trabajadores de servicio dependen de un estricto requisito de seguridad: sólo trabajan bajo HTTPS (o en para el desarrollo). Esto protege a los usuarios de ataques de hombre en medio que podrían manipular el script.

El ciclo de vida del trabajador de servicio

Un trabajador de servicio progresa a través de un ciclo de vida bien definido: registro, instalación, activación y luego manejo de idle/fetch. Entender este ciclo de vida es esencial porque determina cuando sus recursos de caché se ponen disponibles y cuando se limpian los caches viejos.

1. Registro

Antes de que cualquier lógica de Service Worker pueda ejecutarse, su página debe registrar el script. Esto se hace desde su archivo principal JavaScript (normalmente el punto de entrada de la página). El registro informa al navegador de la ubicación y el alcance del Trabajador de Servicio. El alcance define qué URLs puede controlar el Trabajador de Servicio; por defecto es el directorio del script.

if ('serviceWorker' in navigator) {
 navigator.serviceWorker.register('/service-worker.js')
 .then(function(registration) {
 console.log('Service Worker registered with scope:', registration.scope);
 })
 .catch(function(error) {
 console.log('Registration failed:', error);
 });
}

Si el script ya existe, el navegador compara el contenido byte‐for‐byte con la versión instalada previamente. Si ha cambiado, una nueva versión se instala en el fondo mientras que la versión antigua continúa controlando páginas.

2. Instalación

El evento es el primer evento que recibe su Service Worker. Este es el momento perfecto para pre-caminar los archivos esenciales que su aplicación necesita para trabajar fuera de línea: el shell de aplicaciones, CSS núcleo, JavaScript y cualquier página descapotable.

const CACHE_NAME = 'my-cache-v1';
const urlsToCache = [
 '/',
 '/styles/main.css',
 '/scripts/main.js',
 '/offline.html'
];

self.addEventListener('install', function(event) {
 event.waitUntil(
 caches.open(CACHE_NAME)
 .then(function(cache) {
 console.log('Opened cache');
 return cache.addAll(urlsToCache);
 })
 );
});

Si alguno de los archivos no se descargan durante , el paso de instalación falla y el Worker de servicio no se considera instalado. Esta es una característica de seguridad para asegurarse de que nunca tenga una aplicación parcialmente encajada.

3. Activación

Después de la instalación, el navegador espera hasta que todas las páginas que se cargaron con el antiguo trabajador de servicio estén cerradas. Luego se dispara el evento . Este evento se utiliza normalmente para limpiar caches antiguos y para tomar el control de cualquier página que se abrió antes de la activación.

self.addEventListener('activate', function(event) {
 const cacheWhitelist = [CACHE_NAME];
 event.waitUntil(
 caches.keys().then(function(cacheNames) {
 return Promise.all(
 cacheNames.map(function(cacheName) {
 if (cacheWhitelist.indexOf(cacheName) === -1) {
 return caches.delete(cacheName);
 }
 })
 );
 })
 );
});

Llamar dentro del manejador toma inmediatamente el control de todas las páginas abiertas sin necesidad de recarga. Esto se utiliza a menudo durante el desarrollo para una iteración más rápida.

4. Eventos de Fetch

Una vez activado, el Trabajador de Servicio escucha los eventos . Cada vez que el navegador inicia una solicitud de una página controlada, el Trabajador de Servicio puede interceptarlo. Dentro del controlador de eventos de la hembra usted implementa su estrategia de caché de elección.

self.addEventListener('fetch', function(event) {
 event.respondWith(
 caches.match(event.request)
 .then(function(response) {
 // Cache hit - return response
 if (response) {
 return response;
 }
 return fetch(event.request);
 }
 )
 );
});

Estrategias de Caching para diferentes escenarios

Elegir la estrategia de caché adecuada determina el equilibrio entre la frescura y la velocidad. No hay una solución de tamaño individual; los diferentes recursos requieren diferentes enfoques.

Cache‐First (Cache then Network)

Mejor para activos estáticos que raramente cambian: imágenes, fuentes, CSS. El Trabajador de Servicio comprueba primero el caché; si se encuentra, regresa inmediatamente. Si no, se agita de la red y se bloquea la respuesta. Esta estrategia produce cargas de repetición rápidas y relámpagos.

self.addEventListener('fetch', function(event) {
 event.respondWith(
 caches.match(event.request)
 .then(function(response) {
 return response || fetch(event.request).then(function(networkResponse) {
 return caches.open(CACHE_NAME).then(function(cache) {
 cache.put(event.request, networkResponse.clone());
 return networkResponse;
 });
 });
 })
 );
});

Network‐First (Network with Cache Fallback)

Ideal para contenido dinámico: API endpoints, news feeds. Prueba la red primero; si falla o es muy lento, vuelve a la versión en caché. Esto asegura que los usuarios siempre vean los datos más recientes a menos que estén fuera de línea.

self.addEventListener('fetch', function(event) {
 event.respondWith(
 fetch(event.request)
 .then(function(networkResponse) {
 return caches.open(CACHE_NAME).then(function(cache) {
 cache.put(event.request, networkResponse.clone());
 return networkResponse;
 });
 })
 .catch(function() {
 return caches.match(event.request);
 })
 );
});

Stale‐While‐Revalidate

Mejor para los recursos que no tienen que ser instantáneamente frescos: avatares de perfil, RSS feeds. Responder inmediatamente con la versión en caché, pero también buscar una actualización de la red en el fondo. La próxima vez que se solicita el recurso, se sirve la nueva entrada de caché.

self.addEventListener('fetch', function(event) {
 event.respondWith(
 caches.open(CACHE_NAME).then(function(cache) {
 return cache.match(event.request).then(function(cachedResponse) {
 var fetchPromise = fetch(event.request).then(function(networkResponse) {
 cache.put(event.request, networkResponse.clone());
 return networkResponse;
 });
 return cachedResponse || fetchPromise;
 });
 })
 );
});

Network‐Only y Cache‐Only

Para datos sensibles como los detalles bancarios de los usuarios, use Redes solamente] (sin caché). Para el contenido estático puramente fuera de línea (por ejemplo, un manual), ]Cache-Only puede ser suficiente.

Características del trabajo avanzado del servicio

Más allá de la caché, los trabajadores de servicio desbloquean poderosas capacidades de fondo.

Sincronización de antecedentes

Cuando un usuario envía un formulario o envía un mensaje sin conexión, no quieres perder esa acción. le permite registrar un evento de sincronización que el navegador disparará una vez que la conectividad esté de vuelta.

// In page script
navigator.serviceWorker.ready.then(function(registration) {
 return registration.sync.register('sync-messages');
});

// In Service Worker
self.addEventListener('sync', function(event) {
 if (event.tag === 'sync-messages') {
 event.waitUntil(sendPendingMessages());
 }
});

Sincronización de antecedentes es ideal para aplicaciones de chat, presentaciones de formularios o cualquier cola de datos que necesite eventual consistencia.

Notificación de empuje

Los mensajes de empuje llegan desde un servidor y despiertan el Trabajador de Servicio, que luego muestra una notificación. Esto funciona incluso cuando su aplicación web está cerrada, dando a PWAs una sensación nativa.

self.addEventListener('push', function(event) {
 const options = {
 body: 'This notification was triggered by a push message.',
 icon: '/icons/icon-192x192.png',
 badge: '/icons/badge.png',
 data: {
 url: '/new-content'
 }
 };
 event.waitUntil(
 self.registration.showNotification('New Update', options)
 );
});

Al hacer clic en la notificación puede abrir una URL específica usando el evento .

Seguridad y alcance

Los trabajadores de servicio pueden interceptar y modificar las solicitudes, por lo que deben ser atendidos por HTTPS para evitar que se inyecten scripts maliciosos. Durante el desarrollo local, los navegadores permiten a los trabajadores de servicio en como una excepción.

El scopio] de un trabajador de servicio determina qué páginas controla. Por defecto, el alcance es el directorio donde se encuentra el script de trabajo de servicio. Para ampliar el alcance (por ejemplo, controlar todas las páginas bajo ), puede establecer la opción durante el registro. También debe incluir un

Trabajadores de la Depuración

Navegador DevTools es esencial para inspeccionar el comportamiento de los trabajadores de servicio. En Chrome, abrir o ir a la aplicación → Trabajadores de servicio. Usted puede ver el estado de cada trabajador, iniciar / detener manualmente, y registros claros. Firefox ofrece herramientas similares bajo:Debugging.

Consejos comunes de depuración:

  • Actualizar sobre la recarga – en Chrome DevTools, comprobar “Actualizar sobre la recarga” para que un nuevo Worker de Servicio se instale cada vez que la página vuelva a cargar (utilizado durante el desarrollo).
  • Pasaje para red – desactiva temporalmente al Trabajador de Servicio para ver cómo se comporta su aplicación sin ella.
  • Almacenamiento de cuchillas – inspeccionar los recursos en caché en el panel de almacenamiento para verificar su estrategia de caché.
  • Logging] – use dentro del Trabajador de Servicio; los mensajes aparecen en la consola de fondo en DevTools.

Implicaciones de rendimiento

Los trabajadores de servicio pueden mejorar dramáticamente el rendimiento percibido, pero también introducen complejidad. Pre-caching demasiados recursos durante la instalación puede retrasar el paso de la instalación y hacer que la instalación desfallezca si se producen los timeouts. Una buena práctica es cachear sólo la concha mínima en y lazily cache recursos adicionales durante los eventos de embrague.

El tamaño de la caché es limitado por origen (normalmente unos pocos cientos de megabytes en los navegadores móviles). Los archivos de medios de gran tamaño pueden llenar esta cuota. Utilice la API de almacenamiento () para monitorear el uso.

Otra trampa de rendimiento: si su trabajador de servicio realiza una computación de peso pesado dentro del manillador , puede bloquear o retrasar las respuestas de red. Manejadores de captura de mano ligeros y usar patrones asincrónicos.

Pitfalls comunes y cómo evitarlos

  • Contenido de establos de almacenamiento] – siempre verifique sus caches (por ejemplo, , ) y elimine caches antiguos durante el evento . Nunca utilice un nombre de caché sin límites.
  • Página descubierta] – si su página offline no está pre-caída, los usuarios verán una página de error del navegador. Cache un simple retroceso offline durante la instalación.
  • Broken POST requests – Los trabajadores de servicio pueden interceptar las solicitudes POST, pero debe tener cuidado con cachérlas. no soporta POST por defecto; manéjelas por separado o simplemente pasarlas.
  • Missing ] – si no llamas , el navegador tratará de buscar el recurso por sí mismo, superando potencialmente tu lógica de caché.

Ejemplo del mundo real: un simple sin conexión-Ley PWA

Vamos a armar todo. Crearemos un pequeño PWA que encaje su cáscara y proporciona un retroceso sin conexión. El archivo completo de Service Worker ():

const CACHE_NAME = 'pwa-shell-v2';
const SHELL_URLS = [
 '/',
 '/index.html',
 '/styles.css',
 '/app.js',
 '/offline.html'
];

// Install: pre‑cache shell
self.addEventListener('install', function(event) {
 event.waitUntil(
 caches.open(CACHE_NAME)
 .then(function(cache) {
 return cache.addAll(SHELL_URLS);
 })
 );
});

// Activate: clean old caches
self.addEventListener('activate', function(event) {
 const cacheWhitelist = [CACHE_NAME];
 event.waitUntil(
 caches.keys().then(function(cacheNames) {
 return Promise.all(
 cacheNames.map(function(name) {
 if (!cacheWhitelist.includes(name)) {
 return caches.delete(name);
 }
 })
 );
 }).then(function() {
 return self.clients.claim();
 })
 );
});

// Fetch: network‑first for pages, cache‑first for static assets
self.addEventListener('fetch', function(event) {
 if (event.request.mode === 'navigate') {
 // Pages: try network, fall back to cache
 event.respondWith(
 fetch(event.request).then(function(networkResponse) {
 // Update cache with fresh page
 const responseClone = networkResponse.clone();
 caches.open(CACHE_NAME).then(function(cache) {
 cache.put(event.request, responseClone);
 });
 return networkResponse;
 }).catch(function() {
 return caches.match(event.request).then(function(cached) {
 // If page not cached, show offline fallback
 return cached || caches.match('/offline.html');
 });
 })
 );
 } else {
 // Static assets: cache‑first
 event.respondWith(
 caches.match(event.request).then(function(cached) {
 return cached || fetch(event.request).then(function(networkResponse) {
 caches.open(CACHE_NAME).then(function(cache) {
 cache.put(event.request, networkResponse.clone());
 });
 return networkResponse;
 });
 })
 );
 }
});

Este ejemplo utiliza un enfoque de navegación (páginas frescuras cuando no se encuentra en línea, en línea, en el retroceso sin conexión) y en el cache‐first para todos los demás activos.

Conclusión

Service Workers son la columna vertebral de las aplicaciones web modernas sin conexión. Proporcionan una capa de red programable que va mucho más allá de simple caché: sincronización de fondo, notificaciones push, y control completo sobre cómo se sirven los recursos. Al entender el ciclo de vida, elegir la estrategia de caché adecuada para cada tipo de recurso, y siguiendo las mejores prácticas de seguridad, usted puede construir experiencias web que son resilientes, rápidos y atractivos.

Para profundizar en las API, consulte la documentación oficial sobre MDN] y la Google Web Fundamentals guía. Para patrones de caché avanzados, el Offline Cookbook] de Jake Archibald ofrece recetas detalladas.