progressive web apps. what, why and how
TRANSCRIPT
HTML5 is a new Notness
"The biggest mistake we made as a company was be!ng too much on HTML5
as opposed to na"ve.”
This guyFrom Facebook
Native Advantages
✓ Great performance
✓ Smooth anima!ons & interac!ons
✓ Instant loading & offline support
✓ Easy access through home screen
x Distribu=on problems
x Upda=ng is a pain
x Extra care with low memory phone
x Applica=on size
Native disadvantages
Case Studies
75% increase in Tweet Sent
65% increase in pages per session
20% decrease in bounce rate
Our Project✓ Single page app for local meetup videos
✓ Offline support via browser caches
✓ Modern, ES6 JavaScript syntax, no framework
✓ Mul"pla$orm, Android and iOS
✓ Na"ve app look & feel
✓ Fullscreen app
✓ Splash screen
✓ Home screen icon
Engineers.id
Our Plan✓ Design App Shell
✓ Ge!ng The Data from API
✓ Using Service Worker:
✓ Caching App Shell
✓ Caching Data
Engineers.id
✓ Na"ve-like apps:
✓ Standalone app
✓ Adding App Icons
✓ Adding Splas Screen
✓ Deploy and securing our
app
ToolsChrome DevTools - Device Mode✓Simulate device web experiences ✓Responsive breakpoint visualiza"on ✓First meaningful paint, metrics, etc ✓Service worker, manifest, etc
ToolsiOS Simulator✓ localhost is your machine ✓Cri"cal for tes"ng apple-specific features ✓Connect to desktop’s safari for debugging
ToolsLighthouse✓Chrome extension or CLI ✓Checklist for our progressive enhancements ✓Performance sugges"ons
<!DOCTYPE html> <html> <head> <meta charset=utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <meta http-equiv="x-ua-compatible" content="ie-edge"> <meta name="description" content="Best video resources for learn programming"> <link rel="stylesheet" href=“styles/main.css" type="text/css" media="screen" charset="utf-8"> <link rel="stylesheet" href="styles/spinner.css" type="text/css" media="screen" charset="utf-8"> <title>Engieers.id </title> </head>
<body> <nav> <div class="left"> <a href="/"> <h1>Engineers.id </h1> </a> </div> <div class="right"> <button onclick="location.reload()" class="btn card__btn">Refresh </button> </div> </nav> <div class=“spinner”> ... </div> <ul class="cards"> </ul>
<script id="card" type="text/template"> <li class="cards__item"> </li> </script>
</body>
Get The DataStep 2
// scripts/app.js const url = 'https: //engineers-id-backend-ybbwzovhnl.now.sh/api/videos'
fetch(url) .then(response => response.json()) .then(json => { appendData(json) })
fetch Polyfill <!-- index.html --> <html> <head> ... </head> <body> <!-- ... --> <script src="scripts/vendors/fetch.js"> </script> <script src="scripts/app.js"> </script> </body> </html>
Zoom it out!
Naviga"on begins(17ms)
First conten$ul paint (600ms)
First meaningful paint (1.58s)
Time to interac"ve (1.8s)
Service Worker
Not really a new technology
Parallel, not just concurrent
Independent script
Sends message to origin
Register Service WorkerStep 3
// scripts/app.js if ('serviceWorker' in navigator) { navigator.serviceWorker.register('./service-worker.js').then(function () { console.log('Service Worker Registered') }) }
Cache The App ShellStep 4
// service-worker.js var cacheName = ‘engineers-id’ var filesToCache = [ '/', '/index.html', '/scripts/app.js', ‘/styles/main.css', '/styles/spinner.css', ‘/images/logo.svg', // ... ] self.addEventListener('install', function (e) { e.waitUntil( caches.open(cacheName).then(function (cache) { return cache.addAll(filesToCache) }) ) })
Wait, How Service Worker actually work?!Service worker
Installing Ac"vated
Error
Idle Push
Fetch
Terminated
Web page
Caching App Shell
Caching Strategy - Cache Only
//service-worker.js self.addEventListener('fetch', function (event) { // If a match isn't found in the cache, the response // will look like a connection error event.respondWith(caches.match(event.request)) })
Caching Strategy - Network Only
self.addEventListener('fetch', function (event) { event.respondWith(fetch(event.request)) // or simply don't call event.respondWith, which // will result in default browser behaviour })
Caching Strategy - Cache, failing back to network
//service-worker.js self.addEventListener('fetch', function (event) { event.respondWith( caches.match(event.request).then(function (response) { return response || fetch(event.request) }) ) })
Caching Strategy - Cache, NetworK Race // service-worker.js function promiseAny (promises) { return new Promise((resolve, reject) => { promises = promises.map(p => Promise.resolve(p)) promises.forEach(p => p.then(resolve)) promises .reduce((a, b) => a.catch(() => b)) .catch(() => reject(Error('All failed'))) }) }
self.addEventListener('fetch', function (event) { event.respondWith( promiseAny([caches.match(event.request), fetch(event.request)]) ) })
Caching Strategy - Cache Then Network
// service-worker.js self.addEventListener('fetch', function (event) { event.respondWith( caches.open('mysite-dynamic').then(function (cache) { return fetch(event.request).then(function (response) { cache.put(event.request, response.clone()) return response }) }) ) })
Caching Strategy - Network Falling Back To Cache
self.addEventListener('fetch', function (event) { event.respondWith( fetch(event.request).catch(function () { return caches.match(event.request) }) ) })
Cache The ContentStep 5
// service-worker.js self.addEventListener('fetch', e => { e.respondWith( caches.open(dataCacheName).then(cache => { return fetch(e.request) .then(networkResponse => { cache.put(e.request, networkResponse.clone()) return networkResponse }) .catch(() => { return caches.match(e.request) }) }) ) })
App Manifest // manifest.json { "name": "Engineers.id", "short_name": "eng.id", "lang": "en-US", "start_url": "/index.html", "display": "standalone", "theme_color": "#fff", "icons": [ { "src": "images/icons/icon-128x128.png", "sizes": "128x128", "type": "image/png" }, ...
], "background_color": “#fff”, “orientation”: “portrait” }
App Manifest - Home Screen Icons
48 dp icons for home screen and task switcher
144px by 144px
192px by 192px
48px by 48px
96px by96px
App Manifest - Splash Screen Icons
128 dp icons for splash screen
128px by 128px
256px by 256px
512px by 512px
{ "name": "Engineers.id", "short_name": "eng.id", "lang": "en-US", "start_url": "/index.html", "display": "standalone", "theme_color": "#fff", "icons": [], "background_color": “#fff”, “orientation”: “portrait” }
Install Banners
{ "name": "Engineers.id", "short_name": "eng.id", "lang": "en-US", "start_url": "/index.html", "display": "standalone", "theme_color": "#fff", "icons": [
... {
"src": "images/icons/icon-144x144.png", "sizes": "144x144", "type": "image/png" }, ],
"background_color": “#fff”, “orientation”: “portrait” }