Head Instructor & Development Lead at HackerYou
@Rchristiani on Twitter
ryanchristiani.com
letslearnes6.com
NodeSchoolTO Organizer
π¨ππππ π³ π³ π‘
More importantly, offline content.
We have been able to cache connect before with AppCache.
However AppCache is in the process of being removed from the browser, and service workers are taking over.
With Service workers, get the programmatic ability to cache what you want, how you want.
A service worker sits between your site and the network.
π β’ π¦ οΈβ’ βοΈ
This is one component of the Progressive Web App idea.
Service Workers are just one type of Worker. Web Workers in the browser allow us to run code in a separate worker thread.
JavaScript is a single threaded language, meaning it does one thing at a time. But like, pretty fast! However sometimes we need to do more than one thing at a time.
Sorry iOS 9, I used a bunch of arrow functions...and fetch
if('serviceWorker' in navigator) {
navigator.serviceWorker.register('./sw.js')
.then(reg => {
console.log('Service Worker Registered')
})
.catch(err => {
console.log(`Error registering worker ${err}`);
})
}
Application tab to work with service workers.
Inside of service works we have a different environment work with. There is no DOM access available to you.
We can post messages however between our worker and the page.
When a service worker is registered there is a lifecycle it goes there, and there are a series of events that are triggered.
The install event is a good place start your cache. Caching your static assets can be done here.
self.addEventListener('install', function(e) {
caches.open('union-times')
.then((cache) => {
});
});
Available to us in both the worker and the window is CacheStorage, or caches
. This is used to actual open and fill our cache.
caches.open('union-times')
.then((cache) => {
});
Using the .open()
method we provide a name for our cache, it then returns a Promise that we can use to get access to the new cache
.
With this new cache
object we can use the addAll
method to cache our static assets.
return cache.addAll([
'',
'js/script.js',
'js/templater.js',
'css/style.css'
]);
NOTE You have to make sure your paths are correct, otherwise your cache will fail to fetch the resource!
So here we have cached our assets, we can see this if we go to the application panel again, and checkout the cache section.
However we need to now tell the service worker to work as our intermediary, enter the fetch
event.
β£ β β‘ οΈ
π β’ π¦ οΈβ βοΈ
self.addEventListener('fetch', function(e) {
});
Just like other events in the browser, the fetch event is passed an event object.
In the fetch
event we can use the resonpdeWith
method to decide how we want to get our data, from the cache or the network.
self.addEventListener('fetch', function(e) {
e.respondeWith(
);
});
e.respondeWith(
caches.open('union-times')
.then(function(cache) {
})
);
Using the request we can check it see if that request is in our cache.
return caches.match(e.request)
.then(function(res) {
return res;
});
});
If it is, we can return it.
How about caching the content that is new, when the network it up.
return caches.match(e.request)
.then(function(res) {
return res || fetch(e.request).then(res => {
e.request.url
.match('chrome-extension://') ?
'' :
cache.put(e.request, res.clone());
return res;
});
});
β£ β β‘ οΈ
π β’ π¦ β’ βοΈ
β‘ β β£ οΈ
With the current setup, it will just be getting from the cache at this point. Not great for frequently updated content....
self.addEventListener('fetch', function(e) {
e.respondWith(
fetch(e.request)
.then((response) => networkThenCache(response,e))
.catch(() => pullFromCache(e))
);
});
function networkThenCache(response,e) {
return caches.open(cacheVersion)
.then(function(cache) {
return caches.match(e.request)
.then(function(res) {
e.request.url.match('chrome-extension://') ?
'' :
cache.put(e.request, response.clone());
return response;
});
});
}
function pullFromCache(e) {
return caches.open(cacheVersion)
.then(function(cache) {
return caches.match(e.request)
.then(function(res) {
return res;
});
});
}
promises all the way down...
β£ β β‘ οΈ
π π¦ β βοΈβοΈ
β£ β’  β’ β‘ οΈ
β£ β β‘ οΈ
π β’ π¦ οΈβ βοΈ
Now when the user is online, we get fresh info, if the user is offline we get cached info.
Over time we might want add to our cache, or update how our service worker works.
Enter the activate
event.
Activate is fired after the worker has been installed. Once the page loads, the activate event can be used to run any code we want.
First, lets change the hard coded union-times
name we have been using, and store that in a variable,
const cacheVersion = 'union-times-v2';
self.addEventListener('activate', function(e) {
e.waitUntil(
);
});
Get all the caches avaible
self.addEventListener('activate', function(e) {
e.waitUntil(
caches.keys().then((cacheNames) => {
})
);
});
Delete the caches.
self.addEventListener('activate', function(e) {
e.waitUntil(
caches.keys().then((cacheNames) => {
return Promise.all(
cacheNames.filter(() => true)
.map((cache) => caches.delete(cache))
);
})
;
});
Debugging your worker is something you will do a lot of when you start working on it.
The Application tab is your new friend.
A small source of frustration. But it can only get better.
toolbox.precache([
'',
'js/script.js',
'js/templater.js',
'css/style.css'
]);
No need for all the