7. Progressive Web Apps
• Reliability
− Fast loading, works on flaky networks
• First-class
− Do what native does, background
• Engaging
− Home screen access, push notification
16. 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");
25. 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
27. and every navigation and functional event
Install
var navigator.serviceWorker;
sw.register(scriptURL, { scope: scopeURL });
Pre-cache resources
oninstall = e => { /* pre-cache here */ };
Handle fetch event
onfetch = e => { /* respond with magic */ };
Manage Cache
onactivate = e => { /* Deleting cache is on you */ };
Update
registration.update();
28. “/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);
});
29. “/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
30. onactivate
SW
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 register
Evaluate②
installinginstalledactivated
36. 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
38. Cache migration in onactivate
https://jakearchibald.com/2014/offline-cookbook/#on-activate
self.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);
})
);
})
);
});
39. onpush
https://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/');
}
});