Post on 16-Apr-2017
Jungkee SongSenior engineer at Samsung
Web standards authorChromium contributor
Email: jungkee.song@samsung.comGitHub: @jungkees
Progressive Web Apps
• Reliability − Fast loading, works on flaky networks
• First-class − Do what native does, background
• Engaging − Home screen access, push notification
The Total Game Changer!
https://youtu.be/QiXLaisCq10
Udacity Offline-First Web Applications Trailer
A type of web worker
var worker = new Worker(‘dedicated_worker.js');
var worker = new SharedWorker(‘shared_worker.js’);
navigator.serviceWorker.register(“/service_worker.js");
fetch event
Navigation/Resource request
onfetch
Page
SW
Cache Attempt to match cache
Matched Response
Respond to client
Page Page
Event-based worker
Bound to pagePage
Workernew Worker(‘job.js’)
Run job.js
t
Dedicated worker
* Lifetime is bound to the page unless .terminate is called.
Event-drivenPage
SWfetch event
e.g.<img src=‘pic.png’>
Run sw.js
Browser internal
t
Service worker
Term
inate
Start
onfetch e.waitUntil
push eventStart
Term
inate
onpush e.waitUntil
registration active worker waiting worker installing worker
ServiceWorkerRegistration
activeactive
installing
Persistent
Client scope
fetch install
registration.active
BrowserInternals
ScriptSurface
registration.active
registration.installing
ServiceWorkerRegistration
ServiceWorker
ServiceWorker
ServiceWorker
Page Page
HTTPS• SW allows powerful stuffs
− Intercept requests and fabricate your
own responses
• Need to guarantee it has not been tempered
during its own fetch
− localhost is allowed during development
− HTTPS setup is needed to deploy
and every navigation and functional event
Installvar navigator.serviceWorker; sw.register(scriptURL, { scope: scopeURL });
Pre-cache resourcesoninstall = e => { /* pre-cache here */ };
Handle fetch eventonfetch = e => { /* respond with magic */ };
Manage Cacheonactivate = e => { /* Deleting cache is on you */ };
Updateregistration.update();
“/assets/v1” /assets/v1/serviceworker.js
[ Registration map ]Scope Script URL
Register
From page// scope defaults to "/" var sw = navigator.serviceWorker; sw.register("/assets/v1/serviceworker.js") .then(reg => { console.log("success!"); reg.installing.postMessage("Howdy from your installing page."); }) .catch(e => { console.error("Installing the worker failed!:", e); });
“/bar” /bar/sw1.js
[ Registration map ]Scope Script URL
“/foo” /foo/sw.js
Register – Multiple registrations
From�page
var sw = navigator.serviceWorker;
① sw.register('/bar/sw1.js', { scope: '/bar' });
② sw.register('/foo/sw.js', { scope: '/foo' });
③ sw.register('/bar/sw2.js', { scope: '/bar' } );
“/bar” /bar/sw2.js
onactivateSW
oninstall
onfetch
Browser internals
oninstall = e => { // pre-cache, etc.};
onactivate = e => { // upgrade Cache, etc.};
onfetch = e => { // Not yet ready};
Fetched sw script
Fetch sw script
Update① ③ Install④ Activate
onfetch
onfetch = e => { // Not yet ready};
Install process triggered by registerEvaluate②
installinginstalledactivated
onfetchsw.js
Cache Attempt to match cache
Matched response
Respond to client
“/” /sw.js
[ Registration map ]Scope Script URL
“/foo” /foo/sw.js
Page Navigate https://example.com/index.html
fetch event
Scope matching
Run SW
Client matching (navigation)
onfetchsw.js
CacheAttempt to match cache
Matched response
Respond to client
“/” /sw.js
[ Registration map ]Scope Script URL
“/img” /img/sw.js
Page
Fetch “https://example.com/img/flower.png
fetch event
Control
Run SW
Subresource request
Cache of static resources in oninstall
https://jakearchibald.com/2014/offline-cookbook/#on-install-as-a-dependencyself.addEventListener('install', function(event) { event.waitUntil( caches.open('mysite-static-v3').then(function(cache) { return cache.addAll([ '/css/whatever-v3.css', '/css/imgs/sprites-v6.png', '/css/fonts/whatever-v8.woff', '/js/all-min-v4.js' // etc ]); }) ); });
Cache on user demand
document.querySelector('.cache-article').addEventListener('click', function(event) { event.preventDefault();
var id = this.dataset.articleId; caches.open('mysite-article-' + id).then(function(cache) { fetch('/get-article-urls?id=' + id).then(function(response) { // /get-article-urls returns a JSON-encoded array of // resource URLs that a given article depends on return response.json(); }).then(function(urls) { cache.addAll(urls); }); }); });
https://jakearchibald.com/2014/offline-cookbook/#on-user-interaction
Cache of dynamic resource
https://jakearchibald.com/2014/offline-cookbook/#on-network-responseself.addEventListener('fetch', function(event) { event.respondWith( caches.open('mysite-dynamic').then(function(cache) { return cache.match(event.request).then(function (response) { return response || fetch(event.request).then(function(response) { cache.put(event.request, response.clone()); return response; }); }); }) ); });
Cache migration in onactivate
https://jakearchibald.com/2014/offline-cookbook/#on-activateself.addEventListener('activate', function(event) { event.waitUntil( caches.keys().then(function(cacheNames) { return Promise.all( cacheNames.filter(function(cacheName) { // Return true if you want to remove this cache, // but remember that caches are shared across // the whole origin }).map(function(cacheName) { return caches.delete(cacheName); }) ); }) ); });
onpushhttps://jakearchibald.com/2014/offline-cookbook/#on-push-message
self.addEventListener('push', function(event) { if (event.data.text() == 'new-email') { event.waitUntil( caches.open('mysite-dynamic').then(function(cache) { return fetch('/inbox.json').then(function(response) { cache.put('/inbox.json', response.clone()); return response.json(); }); }).then(function(emails) { registration.showNotification("New email", { body: "From " + emails[0].from.name tag: "new-email" }); }) ); } });
self.addEventListener('notificationclick', function(event) { if (event.notification.tag == 'new-email') { // Assume that all of the resources needed to render // /inbox/ have previously been cached, e.g. as part // of the install handler. clients.openWindow(‘/inbox/'); } });
Offline Cookbook
https://jakearchibald.com/2014/offline-cookbook/
Application Shell
• Minimal HTML, CSS, and JavaScript powering UI
• Should
− Load fast
− Be cached
− Dynamically display content
https://developers.google.com/web/updates/2015/11/app-shell
http://stories.flipkart.com/introducing-flipkart-lite/
Standards
Implementation
4th WD in June 2105
Plan a CR in the 3Q of this year
4.0 40+ 44 32+