Raúl Yeguas Frontend developer at Geographica
Progressive Web Apps
Progressive Web App
is PoWAh
Los tres pilares
Confiable
- Servida por HTTPS
- Soporte Offline
- Caché controlada al 100% con Cache API
Rápida
- Carga instantánea sin importar estado de red
- Animaciones y scroll fluidos
Atractiva
- Feeling nativo
- Notificaciones push
- Instalable desde la web
Capa de aplicación vs contenido
Service worker
Web workers
- Ofrecen una interfaz para ejecutar código en hilos en segundo plano.
- Comunicación mediante mensajes.
- Acceso limitado a las APIs de JS (y sin acceso al DOM).
- Útil para trabajo que requiera cálculo o trabajo intensivo evitando afectar a la interfaz.
- Nuevas características: Shared array buffer...
Service workers
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:
- Registrado
- Activado
- Ocioso
- Eliminado
Service workers
app.js
if ('serviceWorker' in navigator) {
window.addEventListener('load', () => {
navigator.serviceWorker.register('/sw.js')
.then(function(sw) {
// Registrado!
})
.catch(function() {
// No registrado :(
});
});
}
Service workers
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,...)
Cache API
Cache API
- Ofrece una interfaz para gestionar la caché de una web de forma manual.
- Es la evolución del Cache Manifest.
- Está orientado a su uso con Service workers, aunque no es obligatorio.
- Permite gestionar varias cachés para una misma web.
Cache API
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
});
);
});
Cache API
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
});
Cache API
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
});
);
});
Integración nativa
Application manifest
{
"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"
}
Notificaciones push
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 )
});
Notificaciones push
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
});
Deploy & install
Vuelta al mundo real
Soporte
Escritorio:
- Google Chrome, Mozilla Firefox, Opera:
- Microsoft Edge, Apple Safari:
.
Móvil:
- Google Chrome (Android):
- Mozilla Firefox, Opera:
- Apple Safari:
Soporte
Y la cosa irá a mejor...
Herramientas y ayudas
Angular Mobile Toolkit
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
Angular Mobile Toolkit (Companion)
push.component.ts
import { NgServiceWorker } from '@angular/service-worker';
...
constructor(public sw: NgServiceWorker) { ... }
...
this.sw.registerForPush({
applicationServerKey: 'A1B2C3...'
})
subscribe( (subscriptionObject) => {
// ...
});
Angular Mobile Toolkit (Cache plugin)
ngsw-manifest.json
{
"static": {
// Autogenerado
"urls": {
"/index.html": "ae543...",
"/app.js": "9ff18...",
"/logo.png": "0e33a...",
...
}
},
"external": {
"urls": [ { "url": "https://fonts.gstatic.com/Roboto.ttf" }, ... ]
}
}
Angular Mobile Toolkit (Push plugin)
ngsw-manifest.json
{
"push": {
"showNotifications": true,
"backgroundOnly": false
}
}
Recibe objetos JSON y pasa las propiedades a las opciones de la API showNotification().
Angular Mobile Toolkit (Custom plugins)
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()
],
});
Angular Mobile Toolkit (Custom plugins)
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
});
}
}
Lighthouse
Herramienta de Google para analizar el rendimiento de aplicaciones web y dar consejos para mejorarlo, con un apartado centrado en PWA.
Mozilla serviceworke.rs
Un auténtico libro de recetas de service workers de la mano de Mozilla que incluye manejo de la Cache API o notificaciones Push:
Is serviceworker ready?
Toda la información acerca del soporte actual de los navegadores a los service workers:
LocalForage
Una librería para aprovechar las APIs de persistencia de datos en el navegador de forma fácil:
Futuro
¿Preguntas?
Gracias
Raúl Yeguas 🔗 raulya.com | 🐦 @neokore