Text
![](https://s3.amazonaws.com/media-p.slid.es/uploads/366798/images/4246246/pasted-from-clipboard.png)
#BDXIO17
Emmanuel DEMEY
@EmmanuelDemey
Zenika Lille
![](https://s3.amazonaws.com/media-p.slid.es/uploads/366798/images/3504898/Y2TWhwYL.jpg)
Gautier de Saint Martin Lacaze
@darkjabberwock
Freelance
![](https://s3.amazonaws.com/media-p.slid.es/uploads/768983/images/4229540/gautier-devfest-2016.png)
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
![](https://s3.amazonaws.com/media-p.slid.es/uploads/366798/images/4262649/pasted-from-clipboard.png)
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
![](https://s3.amazonaws.com/media-p.slid.es/uploads/366798/images/4262650/pasted-from-clipboard.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/366798/images/4263807/pasted-from-clipboard.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/366798/images/4263809/pasted-from-clipboard.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/366798/images/4263810/pasted-from-clipboard.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/366798/images/4263812/pasted-from-clipboard.png)
Caracteristiques
Connectivity-independant
Linkable
Installable
Re-engageable
Discoverable
Fresh
App-Like
Les prérequis
![](https://s3.amazonaws.com/media-p.slid.es/uploads/366798/images/4251671/pasted-from-clipboard.png)
Les prérequis
![](https://s3.amazonaws.com/media-p.slid.es/uploads/366798/images/4246262/https_new_banner.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/366798/images/4246265/breakaway-unikalny-wizerunek.png)
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?
![](https://s3.amazonaws.com/media-p.slid.es/uploads/366798/images/4262690/pasted-from-clipboard.png)
Les fonctionnalités, que nous allons vous présenter, font parties juste d'un toolkit.
Audit
![](https://s3.amazonaws.com/media-p.slid.es/uploads/366798/images/3521985/Pigeon_Point_Lighthouse__2016_.jpg)
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
![](https://s3.amazonaws.com/media-p.slid.es/uploads/768983/images/4229585/pasted-from-clipboard.png)
PW1
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 nous donne un contexte de navigation "top-level"
- 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
- Design Responsive
Conditions de validation d'un Manifest
- Service Worker enregistré
- name ou short_name
- L'url de départ : start_url
- Les icônes : icons (minimum 144px)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/652577/images/3625580/wm-exes.png)
Style
![](https://s3.amazonaws.com/media-p.slid.es/uploads/652577/images/3625584/diff-display.png)
Display
Manifest - Validation
![](https://s3.amazonaws.com/media-p.slid.es/uploads/366798/images/3536416/manifestvalidator.png)
https://manifest-validator.appspot.com
Support
![](https://s3.amazonaws.com/media-p.slid.es/uploads/768983/images/4229596/caniuse-web-app-manifest.png)
Install banner
![](https://s3.amazonaws.com/media-p.slid.es/uploads/366798/images/4257601/pasted-from-clipboard.png)
window.addEventListener(
'beforeinstallprompt', (e) => {
if (isLoggedIn()) {
e.preventDefault();
}
});
Manifest - Event
PW2
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
![](https://s3.amazonaws.com/media-p.slid.es/uploads/366798/images/3593616/Screenshot_from_2017-03-15_17-15-22.png)
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);
PW3
Service Worker
![](https://s3.amazonaws.com/media-p.slid.es/uploads/366798/images/3555253/chrome.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/366798/images/3555255/roue.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/366798/images/3555256/serveur.png)
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
![](https://s3.amazonaws.com/media-p.slid.es/uploads/768983/images/4229606/caniuse-service-worker.png)
PW4
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
- Interface permettant de stocker des paires Request / Response
- API se basant sur les Promise
- Plusieurs caches pour une même application
- Aucun cache implémenté par défaut
- Ajout, mise à jour et suppression à la charge du développeur
App Shell - Cache
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
![](https://s3.amazonaws.com/media-p.slid.es/uploads/366798/images/3561327/Screenshot_from_2017-03-06_21-02-33.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/366798/images/3561329/Screenshot_from_2017-03-06_21-01-32.png)
online / offline
if(!navigator.onLine) {
...
}
window.addEventListener("offline", () => {
...
}, false);
window.addEventListener("online", () => {
...
}, false);
PW5
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
![](https://s3.amazonaws.com/media-p.slid.es/uploads/366798/images/3561264/framed_2013-06-10_22.34.05.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/366798/images/3561265/refresh.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/366798/images/3561313/Screenshot_from_2017-03-06_20-59-56.png)
Support
![](https://s3.amazonaws.com/media-p.slid.es/uploads/768983/images/4229617/caniuse-indexeddb.png)
PW6
Background Sync
![](https://s3.amazonaws.com/media-p.slid.es/uploads/366798/images/4310429/header-1.gif)
- 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');
});
})
PW7
Pour aller plus loin...
- Trusted Web Activities
- Notifications
- Payment Request API
- One-Tap Sign-In / Sign-Up
- Web Share API
![](https://s3.amazonaws.com/media-p.slid.es/uploads/366798/images/4263830/pasted-from-clipboard.png)
Emmanuel DEMEY
@EmmanuelDemey
Zenika Lille
![](https://s3.amazonaws.com/media-p.slid.es/uploads/366798/images/3504898/Y2TWhwYL.jpg)
Gautier de Saint Martin Lacaze
@darkjabberwock
Freelance
![](https://s3.amazonaws.com/media-p.slid.es/uploads/768983/images/4229540/gautier-devfest-2016.png)
Codelab PWA
By Emmanuel Demey
Codelab PWA
- 2,014