Es difícil saber qué hacen los trabajadores de servicio sin comprender su ciclo de vida. Su funcionamiento interno parecerá opaco, incluso arbitrario. Es útil recordar que, al igual que cualquier otra API de navegador, los comportamientos de los trabajadores del servicio están bien definidos y especificados, y permiten aplicaciones sin conexión, a la vez que facilitan las actualizaciones sin interrumpir la experiencia del usuario.
Antes de comenzar a usar Workbox, es importante comprender el ciclo de vida del trabajador de servicio para que lo que hace Workbox tenga sentido.
Definición de términos
Antes de comenzar con el ciclo de vida del trabajador de servicio, es conveniente definir algunos términos sobre cómo funciona ese ciclo.
Control y alcance
La idea de control es fundamental para comprender cómo funcionan los trabajadores de servicio. Una página que se describe como controlada por un service worker es una página que permite que un service worker intercepte solicitudes de red en su nombre. El service worker está presente y puede realizar tareas para la página dentro de un alcance determinado.
Alcance
El alcance de un trabajador de servicio se determina según su ubicación en un servidor web.
Si un service worker se ejecuta en una página ubicada en /subdir/index.html
y se encuentra en /subdir/sw.js
,
el alcance del service worker es /subdir/
.
Para ver el concepto de alcance en acción, consulta este ejemplo:
- Navega a https://service-worker-scope-viewer.glitch.me/subdir/index.html.
Aparecerá un mensaje que indica que ningún service worker controla la página.
Sin embargo, esa página registra un service worker desde
https://service-worker-scope-viewer.glitch.me/subdir/sw.js
. - Vuelve a cargar la página. Como el service worker se registró y ahora está activo, controla la página. Se mostrará un formulario que contiene el alcance, el estado actual y la URL del trabajador de servicio. Nota: Tener que volver a cargar la página no tiene nada que ver con el alcance, sino con el ciclo de vida del trabajador de servicio, que se explicará más adelante.
- Ahora, navega a https://service-worker-scope-viewer.glitch.me/index.html. Aunque se registró un service worker en este origen, aún hay un mensaje que indica que no hay un service worker actual. Esto se debe a que esta página no está dentro del alcance del service worker registrado.
El alcance limita qué páginas controla el service worker.
En este ejemplo, eso significa que el trabajador de servicio cargado desde /subdir/sw.js
solo puede controlar las páginas ubicadas en /subdir/
o su subárbol.
Lo anterior es la forma en que funciona el alcance de forma predeterminada,
pero se puede anular el alcance máximo permitido configurando el
encabezado de respuesta Service-Worker-Allowed
,
así como pasando una
opción scope
al método register
.
A menos que haya una razón muy válida para limitar el alcance del service worker a un subconjunto de un origen, carga un service worker desde el directorio raíz del servidor web para que su alcance sea lo más amplio posible y no te preocupes por el encabezado Service-Worker-Allowed
. De esta manera, es mucho más sencillo para todos.
Cliente
Cuando se dice que un service worker controla una página, en realidad controla un cliente.
Un cliente es cualquier página abierta cuya URL esté dentro del alcance de ese trabajador de servicio.
Específicamente, estas son instancias de un WindowClient
.
El ciclo de vida de un nuevo trabajador de servicio
Para que un trabajador de servicio controle una página, primero debe crearse, por así decirlo. Comencemos con lo que sucede cuando se implementa un nuevo trabajador de servicio para un sitio web sin un trabajador de servicio activo.
Registro
El registro es el paso inicial del ciclo de vida del service worker:
<!-- In index.html, for example: -->
<script>
// Don't register the service worker
// until the page has fully loaded
window.addEventListener('load', () => {
// Is service worker available?
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/sw.js').then(() => {
console.log('Service worker registered!');
}).catch((error) => {
console.warn('Error registering service worker:');
console.warn(error);
});
}
});
</script>
Este código se ejecuta en el subproceso principal y hace lo siguiente:
- Debido a que la primera visita del usuario a un sitio web se produce sin un service worker registrado, espera hasta que la página se cargue por completo antes de registrar uno. Esto evita la contención de ancho de banda si el trabajador del servicio almacena en caché algo.
- Aunque el service worker es compatible con muchos navegadores, una verificación rápida ayuda a evitar errores en los navegadores en los que no es compatible.
- Cuando la página esté completamente cargada y si se admite el service worker, registra
/sw.js
.
Estos son algunos aspectos clave que debes comprender:
- Los service workers solo están disponibles a través de HTTPS o localhost.
- Si el contenido de un trabajador de servicio contiene errores de sintaxis, el registro falla y se descarta el trabajador de servicio.
- Recordatorio: Los trabajadores del servicio operan dentro de un alcance. Aquí, el alcance es todo el origen, ya que se cargó desde el directorio raíz.
- Cuando comienza el registro, el estado del trabajador del servicio se establece en
'installing'
.
Una vez que finalice el registro, comenzará la instalación.
Instalación
Un trabajador de servicio activa su
evento install
después del registro.
Solo se llama a install
una vez por trabajador de servicio y no se volverá a activar hasta que se actualice.
Se puede registrar una devolución de llamada para el evento install
en el alcance del trabajador con addEventListener
:
// /sw.js
self.addEventListener('install', (event) => {
const cacheKey = 'MyFancyCacheName_v1';
event.waitUntil(caches.open(cacheKey).then((cache) => {
// Add all the assets in the array to the 'MyFancyCacheName_v1'
// `Cache` instance for later use.
return cache.addAll([
'/css/global.bc7b80b7.css',
'/css/home.fe5d0b23.css',
'/js/home.d3cc4ba4.js',
'/js/jquery.43ca4933.js'
]);
}));
});
Esto crea una instancia nueva de Cache
y almacena en caché los recursos de antemano.
Tendremos muchas oportunidades para hablar sobre el almacenamiento en caché previo más adelante, así que centrémonos en el rol de event.waitUntil
. event.waitUntil
acepta una promesa y espera hasta que se resuelva.
En este ejemplo, esa promesa realiza dos acciones asíncronas:
- Crea una nueva instancia de
Cache
llamada'MyFancyCache_v1'
. - Después de crear la caché, se precacha un array de URLs de recursos con su método
addAll
asíncrono.
La instalación falla si las promesas que se pasan a event.waitUntil
se rechazan.
Si esto sucede, se descarta el trabajador de servicio.
Si las promesas se resuelven, la instalación se realiza correctamente y el estado del trabajador de servicio cambiará a 'installed'
y, luego, se activará.
Activación
Si el registro y la instalación se realizan correctamente,
se activa el trabajador de servicio y su estado se convierte en 'activating'
Se puede realizar trabajo durante la activación en el
evento activate
del trabajador de servicio.
Una tarea típica en este evento es podar las cachés antiguas, pero para un trabajador de servicio nuevo, esto no es relevante por el momento y se ampliará cuando hablemos de las actualizaciones de los trabajadores de servicio.
En el caso de los trabajadores de servicio nuevos, activate
se activa inmediatamente después de que install
se realiza correctamente.
Una vez que finaliza la activación, el estado del trabajador de servicio se convierte en 'activated'
.
Ten en cuenta que, de forma predeterminada,
el nuevo service worker no comenzará a controlar la página hasta la próxima navegación o actualización de la página.
Controla las actualizaciones de los service workers
Una vez que se implemente el primer trabajador de servicio, es probable que debas actualizarlo más adelante. Por ejemplo, es posible que se requiera una actualización si se producen cambios en el manejo de solicitudes o en la lógica de almacenamiento en caché previo.
Cuándo se producen las actualizaciones
Los navegadores buscarán actualizaciones de un trabajador de servicio en los siguientes casos:
- El usuario navega a una página dentro del alcance del service worker.
- Se llama a
navigator.serviceWorker.register()
con una URL diferente de la del trabajador del servicio instalado actualmente. Sin embargo, no cambies la URL de un trabajador del servicio. - Se llama a
navigator.serviceWorker.register()
con la misma URL que el trabajador del servicio instalado, pero con un alcance diferente. Una vez más, evita esto manteniendo el alcance en la raíz de un origen, si es posible. - Cuando se hayan activado eventos como
'push'
o'sync'
en las últimas 24 horas, pero no te preocupes por estos eventos todavía.
Cómo se realizan las actualizaciones
Es importante saber cuándo el navegador actualiza un trabajador de servicio, pero también lo es el "cómo". Si se supone que la URL o el alcance de un service worker no cambian, un service worker instalado actualmente solo se actualiza a una versión nueva si su contenido cambió.
Los navegadores detectan los cambios de dos maneras:
- Cualquier cambio de byte por byte en las secuencias de comandos que solicite
importScripts
, si corresponde. - Cualquier cambio en el código de nivel superior del trabajador de servicio, que afecta la huella digital que el navegador generó de él.
El navegador hace mucho trabajo pesado aquí. Para asegurarte de que el navegador tenga todo lo que necesita para detectar de forma confiable los cambios en el contenido de un trabajador de servicio, no le digas a la caché de HTTP que la retenga ni cambies su nombre de archivo. El navegador realiza verificaciones de actualización automáticamente cuando se navega a una página nueva dentro del alcance de un trabajador de servicio.
Cómo activar manualmente las verificaciones de actualización
En lo que respecta a las actualizaciones, la lógica de registro no debería cambiar. Sin embargo, una excepción podría ser si las sesiones en un sitio web son de larga duración. Esto puede ocurrir en aplicaciones de una sola página en las que las solicitudes de navegación son poco frecuentes, ya que la aplicación suele encontrar una solicitud de navegación al comienzo de su ciclo de vida. En esas situaciones, se puede activar una actualización manual en el subproceso principal:
navigator.serviceWorker.ready.then((registration) => {
registration.update();
});
En el caso de los sitios web tradicionales o en cualquier caso en el que las sesiones de los usuarios no sean duraderas, es probable que no sea necesario activar actualizaciones manuales.
Instalación
Cuando se usa un empaquetador para generar recursos estáticos, esos recursos contienen valores hash en su nombre, como framework.3defa9d2.js
.
Supongamos que algunos de esos recursos se almacenan en caché previamente para acceder a ellos sin conexión más adelante.
Esto requeriría una actualización del trabajador de servicio para almacenar en caché previamente los recursos actualizados:
self.addEventListener('install', (event) => {
const cacheKey = 'MyFancyCacheName_v2';
event.waitUntil(caches.open(cacheKey).then((cache) => {
// Add all the assets in the array to the 'MyFancyCacheName_v2'
// `Cache` instance for later use.
return cache.addAll([
'/css/global.ced4aef2.css',
'/css/home.cbe409ad.css',
'/js/home.109defa4.js',
'/js/jquery.38caf32d.js'
]);
}));
});
Hay dos diferencias con respecto al primer ejemplo de evento install
anterior:
- Se crea una nueva instancia de
Cache
con una clave de'MyFancyCacheName_v2'
. - Los nombres de los recursos almacenados en caché cambiaron.
Una cosa que debes tener en cuenta es que se instala un trabajador de servicio actualizado junto con el anterior. Esto significa que el trabajador de servicio anterior aún controla las páginas abiertas y, después de la instalación, el nuevo entra en un estado de espera hasta que se activa.
De forma predeterminada, se activará un nuevo trabajador de servicio cuando el anterior no controle a ningún cliente. Esto ocurre cuando se cierran todas las pestañas abiertas del sitio web relevante.
Activación
Cuando se instala un trabajador de servicio actualizado y finaliza la fase de espera,
se activa y se descarta el trabajador de servicio anterior.
Una tarea común que se realiza en el evento activate
de un trabajador del servicio actualizado es podar las cachés antiguas.
Para quitar las cachés antiguas, obtén las claves de todas las instancias de Cache
abiertas con caches.keys
y borra las cachés que no estén en una lista de entidades permitidas definida con caches.delete
:
self.addEventListener('activate', (event) => {
// Specify allowed cache keys
const cacheAllowList = ['MyFancyCacheName_v2'];
// Get all the currently active `Cache` instances.
event.waitUntil(caches.keys().then((keys) => {
// Delete all caches that aren't in the allow list:
return Promise.all(keys.map((key) => {
if (!cacheAllowList.includes(key)) {
return caches.delete(key);
}
}));
}));
});
Las cachés antiguas no se ordenan por sí solas.
Debemos hacerlo nosotros mismos o corremos el riesgo de exceder las cuotas de almacenamiento.
Dado que 'MyFancyCacheName_v1'
del primer trabajador de servicio está desactualizado, la lista de entidades permitidas de la caché se actualiza para especificar 'MyFancyCacheName_v2'
, que borra las cachés con un nombre diferente.
El evento activate
finalizará después de que se quite la caché anterior.
En este punto, el nuevo service worker tomará el control de la página y, finalmente, reemplazará al anterior.
El ciclo de vida continúa
Ya sea que se use Workbox para controlar la implementación y las actualizaciones de los trabajadores del servicio, o se use directamente la API de Service Worker, vale la pena comprender el ciclo de vida de los trabajadores del servicio. Con esa comprensión, los comportamientos de los trabajadores de servicio deberían parecer más lógicos que misteriosos.
Si te interesa profundizar en este tema, te recomendamos que leas este artículo de Jake Archibald. Hay muchos matices en el funcionamiento del ciclo de vida del servicio, pero se puede conocer, y ese conocimiento te será muy útil cuando uses Workbox.