Text
Emmanuel DEMEY
@EmmanuelDemey
GDE Web Technologies
PWAs are the missing technology link to bring the best features of the web to solve the issues of native apps and closed marketplaces.
Chris Heilmann
Alibaba
(e-commerce/China)
- 76% de conversions en plus entre navigateurs;
- 14% en plus d'utilisateurs actifs/mois sous iOS; 30% en plus sous Android;
- 4X plus de taux d'itéraction à partir de "Add to Homescreen".
Source : https://developers.google.com/web/showcase/2016/pdfs/alibaba.pdf
FlipKart
(e-commerce/India)
- Temps passé sur Flipkart lite vs. expériences mobile précedentes: 3.5 minutes vs 70 seconds.
- 3x plus de temps passé sur le site;
- 40% en plus de taux d'engagement
- 70% en plus de taux de conversion entre ceux arrivant via "Add to Homescreen";
- 3x mois d'usage de données.
https://developers.google.com/web/showcase/2016/flipkart
Caracteristiques
Connectivity-independant
Linkable
Installable
Re-engageable
Discoverable
Fresh
App-Like
Les prérequis
Les prérequis
Vous avez dit Progressive ?
if("geolocation" in navigator) {
navigator.geolocation.getCurrentPosition(pos => {
});
} else {
}
- Feature Detection
- Balises ignorées
<meta property="og:title" content="titre"/>
<meta property="og:type" content="website"/>
Par où commencer?
Les fonctionnalités présentées font parties juste d'un toolkit.
Audit
LightHouse
- Outil permettant d'auditer une application web
- Intégré à Chrome Devtools (onglet Audits) ou via un module NPM
- Retourne un score (sur 100) indiquant le taux de conformité avec les PWA
- Règles sur différents domaines : Performance, Sécurité, A11Y, RWD, ...
npm i -g lighthouse
lighthouse https://google.fr
LightHouse
LightHouse
Le Web App Manifest
Le Manifest c'est...
-
Méta-données associées à une web app (format json)
-
Permet "d'installer" une web app
- Il "fait autorité"
Lien vers le Manifest
<!doctype html>
<html lang="fr">
<head>
<meta http-equiv="Content-Type"
content="text/html; charset=utf-8" />
<meta content="width=device-width, initial-scale=1.0"
name="viewport" />
<title>Ma première PWA</title>
<link rel="manifest" href="manifest.json">
</head>
<body>
</body>
</html>
Conditions d'installation
- name meta-data
- Une icône de taille adequate
- Connexion sécurisée
- Doit "fonctionner" sans connexion
- Visites récurrentes
- Service Worker enregisté
Style
Display
Manifest - Validation
https://manifest-validator.appspot.com
Support
Install banner
window.addEventListener(
'beforeinstallprompt', (e) => {
if (isLoggedIn()) {
e.preventDefault();
}
});
Manifest - Event
Service Worker
Service Worker
Service Worker
- Proxy entre votre application et votre serveur
- Nécessite HTTPs
- Pas d'accès au DOM
- Fonctionnalité de Cache, Push, Geofencing, Background Sync
- Evenement d'un SW : install, activate, fetch et sync
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('./sw.js')
.then(req => {
console.log('Success');
}).catch(error => {
console.log('Error');
});
};
Service Worker
self.addEventListener('fetch', event => {
event.respondWith(
fetch(event.request)
);
});
Service Worker
self.addEventListener('fetch', event => {
event.respondWith(
fetch(event.request)
.then(response => {
if(response.status === 404) {
...
}
return response;
})
);
});
interface Request {
method
url
headers
context
referrer
referrerPolicy
mode
credentials
redirect
integrity
cache
bodyUsed
}
self.addEventListener('fetch', event => {
/*
let options = {
status: 200,
statustext: 'OK',
headers: { ... }
};
*/
event.respondWith(
new Response('Hello World', /*opts*/)
);
});
Support
App Shell
- Rendre le layout du site accessible offline
- Afficher la structure du site le plus rapidement possible
- Utilisation de l'API Cache
- Precache des ressources statiques durant l'événement install
- Réutilisation du Cache dans l'événement fetch
App Shell
App Shell - Cache
CacheStorage.open(cacheName)
Cache.match(request, options)
Cache.matchAll(request, options)
Cache.add(request)
Cache.addAll(requests)
Cache.put(request, response)
Cache.delete(request, options)
App Shell - install
const cacheName = 'codelab';
const filesToCache = [
'/',
'/script.js',
'/css/style.css'
];
self.addEventListener('install', e => {
e.waitUntil(
caches.open(cacheName).then(cache => {
return cache.addAll(filesToCache);
})
);
});
App Shell - fetch
self.addEventListener('fetch', e => {
e.respondWith(
caches.match(e.request)
.then(response => {
return response || fetch(e.request);
})
);
});
App Shell - activate
self.addEventListener('activate', e => {
e.waitUntil(
caches.keys().then(keyList => {
return Promise.all(keyList.map(key => {
if (key !== cacheName) {
return caches.delete(key);
}
}));
})
);
});
Prévenir vos utilisateurs
online / offline
if(!navigator.onLine) {
...
}
window.addEventListener("offline", () => {
...
}, false);
window.addEventListener("online", () => {
...
}, false);
IndexedDB
- API permettant de stocker des données côté client (clé => valeur)
- La donnée stockée peut être un objet complexe
- Base de données non relationnelle
- API asynchrone, utilisant des callbacks :(
- Syntaxe assez verbeuse
- Vendor Prefixes
- Cursor
- Indexes
- Transaction
- Utilisation de librairies externes pour simplifier l'utilisation
IndexedDB
const indexedDB = window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB;
// Open (or create) the database
const open = indexedDB.open("Codelab", 1);
// Create the schema
open.onupgradeneeded = function() {
let db = open.result;
let store = db.createObjectStore("Conference", {keyPath: "id"});
let index = store.createIndex("SpeakerIndex", ["speaker"]);
};
open.onsuccess = function() {
// Start a new transaction
let db = open.result;
let tx = db.transaction("Conference", "readwrite");
let store = tx.objectStore("Conference");
let index = store.index("SpeakerIndex");
// Add some data
store.put({id: 12345, speaker: "Manu", "title": "PWA");
store.put({id: 67890, speaker: "Gauthier", "title": "VueJS");
// Query the data
let getPWA = store.get(12345);
let getVueJS = index.get(["Gauthier"]);
getPWA.onsuccess = function() {
console.log(getPWA.result);
};
getVueJS.onsuccess = function() {
console.log(getBob.result);
};
// Close the db when the transaction is done
tx.oncomplete = function() {
db.close();
};
}
- Librairie plus simple à utiliser
- Fallback vers WebSQL et localStorage
- Support des callbacks et des promises
- API similaire à localStorage
LocalForage
localforage.setItem('somekey', {}).then(value => {
console.log(value);
})
localforage.getItem('somekey').then(value => {
console.log(value);
});
localforage.removeItem('somekey').then(() => {
console.log('Key is cleared!');
});
IndexedDB
Support
Background Sync
- Ajout d'un événement Sync aux Service Workers
- Assure l'envoie d'une requête lorsque l'utilisateur est offline
- Traitement exécuté tant que la Promise est rejetée
Background Sync
Background Sync
self.addEventListener('sync', event => {
if (event.tag == 'SYNC_SETTINGS') {
event.waitUntil(updateSettings());
}
});
navigator.serviceWorker.getRegistration()
.then(registration => {
registration.sync.register(
'SYNC_SETTINGS').then(() => {
console.log('Sync registered');
});
})
HTTP2
HTTP1
- 15 ans d'existence
- Applications de plus en plus lourdes
- Navigateur limitant le nombre de requêtes simultanées
- Utilisation de mécanismes permettant de réduire le temps de chargement
- Minification
- Concaténation
- Domain Sharding
HTTP2
- Compatible avec HTTP1
- Fonctionnement avec TLS
- Diminue le nombre d'allers retours
- Compression des Headers
- Limite le nombre de connexions
- Système de multiplexage
- Server Push
HTTP2
HTTP2 avec Express
const fs = require('fs')
const javascript = fs.readFileSync('./app/assets/script.js');
app.get('/', (req, res) => {
res.push('/script.js', {
response: {'content-type': 'application/javascript'}})
.end(javascript)
res.sendFile(__dirname + '/app/index.html');
})
const options = {
key: fs.readFileSync('./server.key'),
cert: fs.readFileSync('./server.crt')
};
require('spdy').createServer(options, app).listen(3002);
Pour aller plus loin...
- Trusted Web Activities
- Notifications
- Payment Request API
- One-Tap Sign-In / Sign-Up
- Web Share API
Emmanuel DEMEY
@EmmanuelDemey
Copy of Codelab PWA
By Emmanuel Demey
Copy of Codelab PWA
- 2,085