Service Workers en JavaScript: La Magia Offline y Notificaciones Push 🚀
Descubre cómo los Service Workers transforman tus aplicaciones web en experiencias offline-first, permitiendo cacheo inteligente de recursos, sincronización en segundo plano y notificaciones push. Este tutorial completo te guiará paso a paso para implementar esta poderosa API.
Introducción a los Service Workers: Tu Proxy Programable en el Navegador ✨
¿Alguna vez te has preguntado cómo algunas aplicaciones web funcionan incluso sin conexión a internet? ¿O cómo recibes notificaciones de una web aunque no la tengas abierta? La respuesta a estas maravillas modernas de la web es: ¡Service Workers!
Un Service Worker es un script de JavaScript que el navegador ejecuta en segundo plano, separado de la página web principal. Actúa como un proxy programable entre tu navegador y la red, interceptando las solicitudes de red, cacheando recursos y gestionando notificaciones push. Esto abre un mundo de posibilidades para construir Progressive Web Apps (PWAs) robustas y de alto rendimiento, capaces de ofrecer una experiencia de usuario similar a la de una aplicación nativa.
¿Por qué son tan importantes los Service Workers? 🎯
Los Service Workers son fundamentales para el desarrollo web moderno por varias razones:
- Experiencia Offline: Permiten que tu aplicación funcione sin conexión, cargando recursos desde la caché en lugar de la red.
- Rendimiento Mejorado: Reducen la dependencia de la red, lo que significa cargas más rápidas y una experiencia más fluida.
- Notificaciones Push: Habilitan la capacidad de enviar notificaciones a los usuarios incluso cuando no están navegando por tu sitio.
- Sincronización en Segundo Plano: Permiten que tu aplicación sincronice datos con el servidor en segundo plano, mejorando la fiabilidad.
- Control Fino sobre las Solicitudes: Te dan un control granular sobre cómo se manejan las solicitudes de red.
Conceptos Clave de los Service Workers 📖
Antes de sumergirnos en el código, es crucial entender algunos conceptos fundamentales:
Ciclo de Vida del Service Worker 🔄
El ciclo de vida de un Service Worker es un proceso bien definido que incluye varios eventos:
- Registro (Registration): El primer paso es registrar tu Service Worker desde tu página principal.
- Instalación (Installation): Una vez registrado, el navegador intenta instalarlo. Durante este evento (
install), puedes precachear recursos esenciales. - Activación (Activation): Después de la instalación exitosa, el Service Worker se activa. El evento
activatees ideal para limpiar cachés antiguas. - Inactivo (Idle): Una vez activado, el Service Worker entra en un estado inactivo hasta que se necesita para manejar un evento (como una solicitud de red o una notificación push).
- Terminación (Termination): El navegador puede terminar un Service Worker para ahorrar memoria si no está en uso.
Estrategias de Caché 📦
El almacenamiento en caché es el corazón de la funcionalidad offline. Aquí algunas estrategias comunes:
- Cache-Only: Siempre sirve la respuesta desde la caché. Útil para recursos estáticos que no cambian.
- Network-Only: Siempre va a la red. Útil para solicitudes que necesitan estar siempre actualizadas.
- Cache, falling back to Network: Intenta obtener de la caché primero. Si falla, va a la red. Una estrategia común para recursos estáticos.
- Network, falling back to Cache: Intenta ir a la red primero. Si falla (offline), sirve desde la caché. Ideal para contenido que idealmente debe ser fresco, pero puede tener una versión offline.
- Cache & Network Race: Compara la velocidad de la caché y la red y usa la primera que responda. A menudo no es óptima por el consumo de recursos.
- Stale-While-Revalidate: Sirve desde la caché inmediatamente, pero también va a la red en segundo plano para actualizar la caché para futuras solicitudes. Excelente para rendimiento y frescura.
Primeros Pasos: Registrando tu Service Worker 🛠️
Para empezar, necesitas dos archivos principales: tu página web (por ejemplo, index.html) y el archivo de tu Service Worker (por ejemplo, sw.js).
Paso 1: Crear el archivo Service Worker (sw.js)
Por ahora, sw.js puede estar vacío o contener solo un console.log para verificar que se carga.
// sw.js
console.log('Service Worker cargado!');
Paso 2: Registrar el Service Worker desde tu página HTML (o JS)
En tu archivo HTML principal (o en un script JavaScript asociado a tu página), debes registrar el Service Worker.
<!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 Service Worker</title>
</head>
<body>
<h1>Hola, Service Workers!</h1>
<script>
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);
});
});
}
</script>
</body>
</html>
Explicación del código:
if ('serviceWorker' in navigator): Verifica si el navegador soporta Service Workers.window.addEventListener('load', ...): Espera a que la página cargue completamente antes de intentar registrar el Service Worker.navigator.serviceWorker.register('/sw.js'): Registra el Service Worker. La ruta/sw.jses relativa al origen del sitio. Un Service Worker puede controlar URLs en su mismo directorio y subdirectorios. Si lo colocas en la raíz, controlará todo el dominio..then()y.catch(): Manejan el éxito y el fallo del registro.
Precacheando Recursos con el Evento install 💾
Una de las funcionalidades más poderosas de los Service Workers es la capacidad de precaching, es decir, guardar recursos en caché tan pronto como el Service Worker se instala. Esto garantiza que ciertos archivos estén disponibles incluso si el usuario está offline.
Vamos a modificar sw.js:
// sw.js
const CACHE_NAME = 'mi-pwa-cache-v1';
const urlsToCache = [
'/',
'/index.html',
'/styles.css',
'/script.js',
'/images/logo.png'
];
self.addEventListener('install', event => {
console.log('Service Worker: Evento install');
event.waitUntil(
caches.open(CACHE_NAME)
.then(cache => {
console.log('Service Worker: Cache abierta');
return cache.addAll(urlsToCache);
})
.then(() => self.skipWaiting())
.catch(error => {
console.error('Service Worker: Fallo en el precaching', error);
})
);
});
self.addEventListener('activate', event => {
console.log('Service Worker: Evento activate');
// Una vez activado, toma el control de las páginas existentes
event.waitUntil(self.clients.claim());
});
self.addEventListener('fetch', event => {
console.log('Service Worker: Evento fetch para', event.request.url);
event.respondWith(
caches.match(event.request)
.then(response => {
// Si el recurso está en caché, lo servimos
if (response) {
return response;
}
// Si no, vamos a la red
return fetch(event.request);
})
);
});
Analizando el código:
CACHE_NAMEyurlsToCache: Definimos un nombre para nuestra caché (útil para versionar y limpiar cachés antiguas) y una lista de URLs que queremos precachear.self.addEventListener('install', event => { ... }):- Este evento se dispara cuando el Service Worker se instala.
event.waitUntil(): Se asegura de que el navegador no termine el Service Worker hasta que la promesa dentro dewaitUntilse resuelva. Esto es crucial para operaciones asíncronas.caches.open(CACHE_NAME): Abre una caché con el nombre especificado. Si no existe, la crea.cache.addAll(urlsToCache): Descarga todos los recursos deurlsToCachey los añade a la caché. Si alguna falla, toda la operaciónaddAllfalla.self.skipWaiting(): Obliga al Service Worker a activarse inmediatamente, sin esperar a que todas las pestañas abiertas que controla se cierren. Útil en desarrollo.
Ejemplo de Estructura de Archivos
Para que el ejemplo funcione, asegúrate de tener una estructura de archivos similar a esta:
├── index.html
├── sw.js
├── styles.css
├── script.js
└── images
└── logo.png
Interceptando Solicitudes con el Evento fetch 🌐
El evento fetch es donde los Service Workers realmente muestran su poder. Se dispara cada vez que el navegador realiza una solicitud de red para cualquier recurso dentro del alcance del Service Worker. Aquí puedes interceptar la solicitud y decidir cómo responder.
En el sw.js anterior, ya hemos implementado una estrategia básica:
// ... (código anterior)
self.addEventListener('fetch', event => {
console.log('Service Worker: Evento fetch para', event.request.url);
event.respondWith(
caches.match(event.request)
.then(response => {
// Estrategia: Cache, falling back to Network
if (response) {
return response; // Si está en caché, la servimos
}
// Si no está en caché, intentamos obtenerla de la red
return fetch(event.request)
.then(networkResponse => {
// Opcional: añadir recursos nuevos a la caché
// Esto podría hacerse de forma más selectiva
// cache.put(event.request, networkResponse.clone());
return networkResponse;
})
.catch(() => {
// Si la red falla y no hay caché, podemos servir una página offline
return caches.match('/offline.html');
});
})
);
});
Explicación de fetch más detallada:
event.respondWith(Promise): Toma una promesa que resuelve en unaResponse. El Service Worker usará esta respuesta para la solicitud. Si la promesa falla, la solicitud de red también falla.caches.match(event.request): Busca la solicitud en todas las cachés que el Service Worker puede ver. Devuelve una promesa que resuelve en laResponsecoincidente oundefinedsi no se encuentra.fetch(event.request): Realiza la solicitud de red real, como si no hubiera un Service Worker.
Estrategia: Network, falling back to Cache (con actualización de caché)
Esta estrategia intenta primero obtener el recurso de la red para asegurar que esté fresco. Si la red falla (por ejemplo, el usuario está offline), recurre a la versión en caché.
// sw.js
// ... (código install y activate)
self.addEventListener('fetch', event => {
// Ignorar solicitudes que no son GET o de extensiones
if (event.request.method !== 'GET' || event.request.url.startsWith('chrome-extension://')) {
return;
}
event.respondWith(
fetch(event.request)
.then(networkResponse => {
// Si la solicitud de red fue exitosa, la almacenamos en caché y la devolvemos
return caches.open(CACHE_NAME).then(cache => {
cache.put(event.request, networkResponse.clone());
return networkResponse;
});
})
.catch(() => {
// Si la red falla, buscamos en la caché
return caches.match(event.request)
.then(cachedResponse => {
if (cachedResponse) {
return cachedResponse;
}
// Si no hay caché y la red falla, podemos servir una página offline genérica
return caches.match('/offline.html');
});
})
);
});
Actualización y Limpieza de Caché con el Evento activate 🧹
Cuando despliegas una nueva versión de tu aplicación, necesitas que el Service Worker se actualice y limpie las cachés antiguas para evitar servir contenido obsoleto. El evento activate es el lugar perfecto para esto.
El Problema de la Nueva Versión
Cuando subes un nuevo sw.js al servidor, el navegador lo detecta como una nueva versión. El nuevo Service Worker se instala en segundo plano, pero no se activa inmediatamente. Permanece en estado waiting hasta que:
- Todas las pestañas que controla el Service Worker actual se cierren.
- Si usaste
self.skipWaiting()durante la instalación del nuevo Service Worker.
Una vez que el nuevo Service Worker se activa, el viejo se desactiva. En el evento activate del nuevo Service Worker, es el momento de limpiar las cachés antiguas.
Limpieza de Cachés en el Evento activate
// sw.js
const CACHE_NAME = 'mi-pwa-cache-v2'; // Incrementamos la versión de la caché
const urlsToCache = [
'/',
'/index.html',
'/styles.css?v=2', // Podemos versionar recursos para asegurar que se actualicen
'/script.js?v=2',
'/images/logo.png'
];
self.addEventListener('install', event => {
console.log('Service Worker: Evento install (v2)');
event.waitUntil(
caches.open(CACHE_NAME)
.then(cache => {
console.log('Service Worker: Cache v2 abierta');
return cache.addAll(urlsToCache);
})
.then(() => self.skipWaiting())
);
});
self.addEventListener('activate', event => {
console.log('Service Worker: Evento activate (v2)');
event.waitUntil(
caches.keys().then(cacheNames => {
return Promise.all(
cacheNames.map(cacheName => {
if (cacheName !== CACHE_NAME) {
console.log('Service Worker: Eliminando caché antigua', cacheName);
return caches.delete(cacheName);
}
})
);
})
.then(() => self.clients.claim())
);
});
// ... (evento fetch sin cambios)
Explicación de la limpieza:
- Incrementamos
CACHE_NAMEami-pwa-cache-v2. Esto es crucial para que el nuevo Service Worker cree una caché diferente a la antigua. - En el evento
activate:caches.keys(): Obtiene un array con los nombres de todas las cachés que tiene el origen.- Filtramos y eliminamos todas las cachés cuyos nombres no coinciden con nuestro
CACHE_NAMEactual (mi-pwa-cache-v2). Promise.all(): Asegura que todas las promesas de eliminación de caché se resuelvan antes de quewaitUntiltermine.self.clients.claim(): Permite que el Service Worker recién activado tome el control de las páginas no controladas (páginas abiertas que estaban siendo controladas por el SW anterior) inmediatamente, sin necesidad de recargar la página.
Notificaciones Push con Service Workers 💬
Las notificaciones Push permiten a tu aplicación web enviar mensajes a los usuarios incluso cuando no están usando activamente tu sitio. Esto es posible gracias a una combinación de APIs: Service Workers, la API de Notificaciones y la API Push.
¿Cómo funcionan las Notificaciones Push?
- Registro: El usuario debe dar permiso para recibir notificaciones.
- Suscripción: Tu aplicación web, a través del Service Worker, obtiene un
PushSubscriptiondel navegador. Este objeto contiene una URL de punto final (endpoint) y una clave de cifrado. - Servidor: El
PushSubscriptionse envía a tu servidor backend y se almacena en una base de datos. - Envío: Cuando quieres enviar una notificación, tu servidor usa el
PushSubscriptionpara enviar una solicitud a un servicio push (como FCM de Google, WebPush de Mozilla, etc.). - Recepción: El servicio push entrega el mensaje al navegador del usuario.
- Service Worker: El Service Worker del usuario recibe el evento
push. - Visualización: El Service Worker muestra la notificación al usuario usando
self.registration.showNotification().
Paso 1: Pedir Permiso y Suscribirse (en la página principal script.js)
En tu script.js (o en la sección <script> de index.html):
// script.js
async function subscribeUser() {
if (!('serviceWorker' in navigator && 'PushManager' in window)) {
console.warn('Push messaging no soportado.');
return;
}
const registration = await navigator.serviceWorker.ready;
try {
const subscription = await registration.pushManager.subscribe({
userVisibleOnly: true,
applicationServerKey: urlBase64ToUint8Array('TU_CLAVE_PUBLICA_VAPID_AQUI')
});
console.log('Suscripción Push exitosa:', JSON.stringify(subscription));
// Aquí deberías enviar la suscripción a tu servidor backend
// await fetch('/api/subscribe', { method: 'POST', body: JSON.stringify(subscription) });
} catch (error) {
console.error('Fallo en la suscripción Push:', error);
}
}
function urlBase64ToUint8Array(base64String) {
const padding = '='.repeat((4 - base64String.length % 4) % 4);
const base64 = (base64String + padding)
.replace(/\-/g, '+')
.replace(/_/g, '/');
const rawData = window.atob(base64);
const outputArray = new Uint8Array(rawData.length);
for (let i = 0; i < rawData.length; ++i) {
outputArray[i] = rawData.charCodeAt(i);
}
return outputArray;
}
// Llamar a esta función cuando el usuario quiera suscribirse
// Por ejemplo, con un botón o después de una interacción del usuario
// document.getElementById('subscribe-button').addEventListener('click', subscribeUser);
Generando Claves VAPID:
Necesitarás un par de claves VAPID (Voluntary Application Server Identification) para la autenticación de tu servidor con el servicio push. Puedes generarlas con herramientas como web-push de Node.js:
npx web-push generate-vapid-keys
Esto te dará una clave pública y una privada. La clave pública es la que usarás en applicationServerKey.
Paso 2: Manejar el Evento push en el Service Worker (sw.js)
// sw.js
// ... (código install, activate, fetch)
self.addEventListener('push', event => {
console.log('Service Worker: Evento push recibido.');
let data = {};
if (event.data) {
data = event.data.json();
}
const title = data.title || '¡Algo nuevo!';
const options = {
body: data.body || 'Has recibido una notificación.',
icon: data.icon || '/images/logo.png',
badge: data.badge || '/images/badge.png',
data: data.url || '/', // URL a abrir al hacer click
actions: [
{ action: 'explorar', title: 'Explorar' },
{ action: 'cerrar', title: 'Cerrar' }
]
};
event.waitUntil(
self.registration.showNotification(title, options)
);
});
self.addEventListener('notificationclick', event => {
console.log('Service Worker: Notificación clickeada.');
event.notification.close();
// Abrir una nueva ventana o enfocar una existente
if (event.action === 'explorar' || event.action === 'default') {
event.waitUntil(
clients.openWindow(event.notification.data)
);
} else if (event.action === 'cerrar') {
console.log('Notificación cerrada por el usuario.');
}
});
Explicación de los eventos push:
push: Se dispara cuando el navegador recibe un mensaje push. El Service Worker está dormido y se despierta para manejar esto.event.data.json(): Recupera los datos enviados con la notificación (si son JSON).self.registration.showNotification(title, options): Muestra la notificación al usuario.optionspermite configurar el cuerpo, icono, acciones, etc.event.waitUntil(): Asegura que la notificación se muestre incluso si el Service Worker se va a dormir.
notificationclick: Se dispara cuando el usuario hace clic en la notificación o en una de sus acciones.event.notification.close(): Cierra la notificación.clients.openWindow(url): Abre una nueva pestaña o enfoca una existente con la URL especificada.
Depuración de Service Workers 🐞
Los Service Workers pueden ser un poco complicados de depurar debido a su naturaleza asíncrona y al margen entre el navegador y la red. Afortunadamente, las herramientas de desarrollador ofrecen un gran soporte.
Herramientas del Desarrollador (Chrome/Edge)
-
Application Panel: Ve a
DevTools>Application>Service Workers. Aquí puedes:- Ver el estado de tu Service Worker (registrado, activado, esperando).
Unregister: Eliminar el Service Worker.Update on reload: Actualizar el Service Worker cada vez que recargas la página.Skip Waiting: Forzar la activación de un Service Worker en estadowaiting.- Ver las URLs que controla.
- Enlace a los
Sourcedel SW para depuración.
-
Cache Storage: En
Application>Cache Storage, puedes inspeccionar los contenidos de las cachés creadas por tu Service Worker. Puedes ver los recursos almacenados y eliminarlos. -
Network Panel: Cuando el Service Worker está activo, verás una columna
Sizeen el panelNetworkque indica(from ServiceWorker). Esto te confirma que el recurso se sirvió desde la caché del Service Worker.
Captura de pantalla de DevTools (Application Panel)
Consejos para la Depuración
console.log(): Útil para seguir el flujo de los eventosinstall,activateyfetch.self.skipWaiting()yself.clients.claim(): Usa estas funciones durante el desarrollo para forzar la activación y el control inmediato, facilitando las pruebas. Retíralas o considéralas cuidadosamente en producción.- Desactivar cachés en DevTools: En el panel
Network, la opciónDisable cacheno deshabilita la caché del Service Worker. Para una recarga completa sin SW, usa Ctrl + Shift + R (recarga forzada). - Versión de caché: Siempre incrementa el
CACHE_NAMEcuando hagas cambios significativos en los recursos precacheados o en la lógica de caché para asegurar que los usuarios obtengan la versión más reciente.
Ejemplos de Estrategias de Caché Avanzadas 🚀
Exploremos algunas estrategias de caché más sofisticadas que puedes implementar en el evento fetch.
Estrategia 1: Stale-While-Revalidate
Esta estrategia sirve el recurso desde la caché inmediatamente, mientras que en segundo plano, va a la red para obtener una versión fresca y actualizar la caché para futuras solicitudes. Es excelente para recursos que no necesitan ser súper actuales, pero se benefician de una carga rápida y una actualización posterior.
// sw.js
// ... (código install y activate)
self.addEventListener('fetch', event => {
// Ignorar solicitudes que no son GET o de extensiones
if (event.request.method !== 'GET' || event.request.url.startsWith('chrome-extension://')) {
return;
}
event.respondWith(
caches.open(CACHE_NAME).then(cache => {
return cache.match(event.request).then(cachedResponse => {
const fetchPromise = fetch(event.request).then(networkResponse => {
// Actualizamos la caché con la respuesta de la red
cache.put(event.request, networkResponse.clone());
return networkResponse;
});
// Devolvemos la caché si existe, de lo contrario, esperamos la red
return cachedResponse || fetchPromise;
});
})
);
});
Estrategia 2: Cache then Network
Esta estrategia es útil para datos que se actualizan con frecuencia pero que también necesitan una experiencia offline. Sirve la versión cacheadas inmediatamente y luego, después de que se carga la página, va a la red para obtener la versión más reciente y actualizar la UI si es necesario. Esto generalmente requiere coordinación con la página principal (usando postMessage).
En sw.js:
// sw.js
// ... (código install y activate)
self.addEventListener('fetch', event => {
if (event.request.method !== 'GET' || event.request.url.startsWith('chrome-extension://')) {
return;
}
// Solo para solicitudes de datos específicos, ej. una API JSON
if (event.request.url.includes('/api/')) {
event.respondWith(async function() {
const cache = await caches.open(CACHE_NAME);
const cachedResponse = await cache.match(event.request);
const networkResponsePromise = fetch(event.request);
event.waitUntil(async function() {
const networkResponse = await networkResponsePromise;
await cache.put(event.request, networkResponse.clone());
// Opcional: notificar a la página que hay nuevos datos
const clients = await self.clients.matchAll();
clients.forEach(client => client.postMessage({
type: 'CACHE_UPDATED',
url: event.request.url
}));
}());
return cachedResponse || networkResponsePromise;
}());
}
});
En la página principal (script.js):
// script.js
// ... (código de suscripción push)
if ('serviceWorker' in navigator) {
navigator.serviceWorker.addEventListener('message', event => {
if (event.data && event.data.type === 'CACHE_UPDATED') {
console.log('Datos actualizados en caché:', event.data.url);
// Aquí puedes recargar una sección de la UI o mostrar un mensaje
// alert('Hay nuevos datos disponibles! Recargando...');
// window.location.reload();
}
});
}
// Ejemplo de cómo cargar datos inicialmente (página web)
async function loadData() {
const response = await fetch('/api/data');
const data = await response.json();
console.log('Datos cargados:', data);
// Renderizar datos en la UI
}
// loadData(); // Llamar al cargar la página
Consideraciones Adicionales y Buenas Prácticas ✅
- Mantenimiento: Versiona tu caché (
CACHE_NAME) y limpia las cachés antiguas en el eventoactivatepara evitar problemas con versiones anteriores de tu aplicación. - Tamaño del Service Worker: Mantén tu archivo
sw.jslo más pequeño posible, ya que se ejecuta en un hilo separado y su carga inicial es crítica. - Alcance (Scope): La ubicación de tu
sw.jsdefine su alcance. Si lo pones en la raíz del dominio (/sw.js), controlará todas las páginas. Si lo pones en/blog/sw.js, solo controlará URLs bajo/blog/. - User Experience (UX): Informa al usuario cuando la aplicación está offline o cuando hay una nueva versión disponible para recargar. Una Página Offline Personalizada es un must-have.
- Workbox: Para proyectos más grandes, considera usar Workbox. Es una caja de herramientas de Google que facilita la gestión de Service Workers con estrategias de caché predefinidas, enrutamiento, pre-cacheo y más, reduciendo la cantidad de código boilerplate que necesitas escribir.
Conclusión: Empoderando tus Web Apps con Service Workers 🎉
Los Service Workers son una pieza fundamental en la construcción de Progressive Web Apps (PWAs) robustas, rápidas y fiables. Al actuar como un proxy programable, te permiten tomar el control de la experiencia de red, habilitando funcionalidades como el funcionamiento offline, notificaciones push, y una mejora sustancial en el rendimiento.
Hemos cubierto desde el registro y el ciclo de vida, pasando por el precaching de recursos estáticos, la intercepción de solicitudes fetch con diversas estrategias de caché, hasta la implementación de notificaciones push. La depuración, aunque puede parecer intimidante al principio, es manejable con las herramientas adecuadas del navegador.
Adoptar Service Workers significa ofrecer a tus usuarios una experiencia web que se siente tan nativa como una aplicación instalada. ¡Ahora tienes el conocimiento para empezar a construir tu propia PWA con la magia offline y las notificaciones push!
Tutoriales relacionados
- Almacenamiento Web con JavaScript: Cookies, LocalStorage, SessionStorage e IndexedDBintermediate15 min
- Desarrollando Micro-Frontends con JavaScript: Módulos Independientes para Webs Escalablesintermediate18 min
- Desentrañando 'this' en JavaScript: Contexto de Ejecución y Enlaceintermediate18 min
- Web Components: Construyendo Componentes Reutilizables y Encapsulados en JavaScriptintermediate15 min
- Proxy y Reflect en JavaScript: Intercepta Operaciones de Objetos con Poder 🚀advanced18 min
Comentarios (0)
Aún no hay comentarios. ¡Sé el primero!