Raúl Yeguas Frontend developer at Geographica
Raúl Yeguas Frontend developer at Geographica

Es un tipo especial de worker dedicado principalmente a controlar la navegación y el acceso a datos en una web.
Es asíncrono, por lo que no hay acceso a XHR síncrono o localStorage.
Ciclo de vida:
app.js
if ('serviceWorker' in navigator) {
window.addEventListener('load', () => {
navigator.serviceWorker.register('/sw.js')
.then(function(sw) {
// Registrado!
})
.catch(function() {
// No registrado :(
});
});
}
sw.js
this.addEventListener('install', (event) => {
event.waitUntil(
....
// Gestión de caché, descarga de recursos de app shell, ...
);
});
// Suscripción a otros eventos (fetch, activate,...)
sw.js
const cacheName = 'cache-v1'; // Nombre de caché (para versionar)
const cacheFiles = [...]; // Ficheros de la app shell
this.addEventListener('install', (event) => {
event.waitUntil(
caches.open(cacheName) // Abrimos la caché
.then( (cache) => {
return cache.addAll(cacheFiles); // Agregamos todos los ficheros necesarios
});
);
});
sw.js
this.addEventListener('activate', (event) => {
event.waitUntil(
caches.keys() // Obtenemos todas las claves de caché
.then( (keyList) => {
return Promise.all(keyList.map( (key) => {
if (key !== cacheName) { // Si no pertenece a la caché nueva ...
return caches.delete(key); // ... la borramos
}
}));
});
);
return self.clients.claim(); // Para tomar el control de cachés antiguas
});
sw.js
this.addEventListener('fetch', (event) => {
event.respondWith(
caches.match(event.request) // Consultamos si existe, si no, resuelve 'undefined'
.then( (response) => {
return response || fetch(event.request); // Devolvemos de la caché o descargamos
})
.catch( () => { // Si todo falla (no hay red, por ej.)...
return caches.match('/assets/fallback.jpg'); // Podemos devolver algo por defecto
});
);
});
{
"name": "My application",
"short_name": "MyAPP",
"icons": [{
"src": "images/icons/icon-128x128.png",
"sizes": "128x128",
"type": "image/png"
}, ... ],
"start_url": "/index.html",
"display": "standalone",
"background_color": "#3E4EB8",
"theme_color": "#2F3BA2"
}
La interfaz pushManager
navigator.serviceWorker.ready.then( (serviceWorkerRegistration) => {
// Opciones (no abligatorias) para subscribe o permissionState
const opts = {
userVisibleOnly: true,
applicationServerKey: 'ABCD1234...'
}
// Subscribirse a un servidor Push
serviceWorkerRegistration.pushManager.subscribe( opts )
// Obtener suscripción activa
serviceWorkerRegistration.pushManager.getSubscription()
// Comprueba el permiso del usuario a usar notificaciones Push
serviceWorkerRegistration.pushManager.permissionState( opts )
});
Escuchar notificaciones Push
sw.js
self.addEventListener('push', (event) => { // Escuchamos eventos push
const obj = event.data.json(); // Podemos obtener los datos en json
console.log(obj); // Y hacer con ellos lo que queramos
});
Escritorio:


.
Móvil:



Y la cosa irá a mejor...
Paquete para crear/convertir una aplicación escrita en Angular en una PWA, basada en plugins y configuración
Elementos clave: Angular ServiceWorker Companion y ngsw-manifest.json
Instalación:
$ > ng new my-pwa
$ > npm install --save @angular/service-worker
$ > ng set apps.0.serviceWorker=true
push.component.ts
import { NgServiceWorker } from '@angular/service-worker';
...
constructor(public sw: NgServiceWorker) { ... }
...
this.sw.registerForPush({
applicationServerKey: 'A1B2C3...'
})
subscribe( (subscriptionObject) => {
// ...
});
ngsw-manifest.json
{
"static": {
// Autogenerado
"urls": {
"/index.html": "ae543...",
"/app.js": "9ff18...",
"/logo.png": "0e33a...",
...
}
},
"external": {
"urls": [ { "url": "https://fonts.gstatic.com/Roboto.ttf" }, ... ]
}
}
ngsw-manifest.json
{
"push": {
"showNotifications": true,
"backgroundOnly": false
}
}
Recibe objetos JSON y pasa las propiedades a las opciones de la API showNotification().
worker-custom.js
import { bootstrapServiceWorker } from '@angular/service-worker/worker';
import { ... } from '@angular/service-worker/plugins/...';
import { MyNGSWPlugin } from './plugins/my-ngsw-plugin';
bootstrapServiceWorker({
manifestUrl: 'ngsw-manifest.json',
plugins: [
// Indicamos los plugins de Angular SW que usaremos
...
MyNGSWPlugin()
],
});
my-ngsw-plugin.js
export function MyNGSWPlugin () {
return (worker) => new MyNGSWPluginImpl(worker);
}
export class MyNGSWPluginImpl {
setup (opts) {}
constructor () {
self.addEventListener('cualquier-evento-sw', (event) => {
// Implementamos nuestro listener
});
}
}
Herramienta de Google para analizar el rendimiento de aplicaciones web y dar consejos para mejorarlo, con un apartado centrado en PWA.

Un auténtico libro de recetas de service workers de la mano de Mozilla que incluye manejo de la Cache API o notificaciones Push:
Toda la información acerca del soporte actual de los navegadores a los service workers:
Una librería para aprovechar las APIs de persistencia de datos en el navegador de forma fácil:
Raúl Yeguas 🔗 raulya.com | 🐦 @neokore