Caching is one (just one) of the things that service workers do really well. A good caching policy enables your web app to do some pretty cool things, like serving almost instantaneous responses, smoothing over patchy network connections, and even providing offline functionality. With the right caching you can banish 404 response pages to the annals of web history.
If you want to go nuclear in terms of performance of your web apps, check out our amp-install-serviceworker article, although this will require a bit more commitment to get set up. Or if you want a more nuts-and-bolts look at service worker caching, without the use of helper libraries, then see our Taking the web offline with service workers article. In this article, we’re just taking a look at the easiest and quickest options to get some caching into your existing website.
But is there a really simple way to implement caching with service workers right now? Let’s find out.
sw-toolbox
Probably the most versatile and easy-to-use library to help you is the sw-toolbox
library. It offers a simple way to achieve the most common caching and routing tasks that you might need. The sw-toolbox
is open source and you can grab it in one of the following ways:
1 |
bower install --save sw-toolbox |
1 |
npm install --save sw-toolbox |
1 |
git clone https://github.com/GoogleChrome/sw-toolbox.git |
To use sw-toolbox
you’ll need to deploy it with your project.
So, first we’re going to write a short JavaScript file—the service worker—and save it as sw.js
. You can find out more about service workers here, but right now you can think of them as programmable caching and routing network proxies that can intercept and manipulate network requests whatever way you like.
In this example we are going to save sw.js
under the caching
directory, to keep it separate from other examples we host. Since the location of this file determines its scope (i.e. what directories it has control over), you might prefer to save it at the root /
directory of your web project.
To import sw-toolbox
into your service worker, add the following line to the top of your service worker source (your path might be different depending on how you installed sw-toolbox
):
1 |
importScripts('/lib/node_modules/sw-toolbox/sw-toolbox.js'); |
Caching strategies
The sw-toolbox
offers several out-of-the-box caching policies. These include:
networkOnly
– only fetch from networkcacheOnly
– only fetch from cachefastest
– fetch from both, and respond with whichever comes firstnetworkFirst
– fetch from network, if that fails, fetch from cachecacheFirst
– fetch from cache, but also fetch from network and update cache
These behave more or less as you’d expect, as summarised above. There are subtleties though. For instance, fastest
will always make a network request, and if there is something new on the network, then the cache will be automatically updated. You can read all about these strategies in detail in Jake Archibald’s Offline Cookbook.
So, how to implement one of these in your app? Easy, just add the following line to your service worker code, for example:
1 |
toolbox.router.default = toolbox.cacheFirst; |
We’ll choose cacheFirst
here, as it’s a good fit for a blog type site. If you don’t get the latest content this time, you’ll get it next time.
So that’s the service worker: it can cache some resources, and we’re now serving those cached resource. With this caching strategy, whenever an item is retrieved from the network, the cache is automatically updated.
The full service worker code, all two lines of it, is here:
1 2 3 |
importScripts('../lib/node_modules/sw-toolbox/sw-toolbox.js'); toolbox.router.default = toolbox.cacheFirst; |
Now we just need to add the service worker to our HTML page, and we’re done. Add this code to the head
of your HTML document and you’re good to go:
1 2 3 4 5 6 7 8 9 10 |
<script type="text/javascript"> var sw = "https://mobiforge.github.io/caching/sw-cacher.js"; if("serviceWorker" in navigator) { navigator.serviceWorker.register(sw).then(function(reg){ console.log('ServiceWorker scope: ', reg.scope); }).catch(function(err) { console.log('ServiceWorker registration failed: ', err); }); }; </script> |
Precaching
With the setup above, the following sequence occurs:
- First page request – handled by server, web page – service worker installed
- Second page request – handled by service worker, makes request to server for content, caches resources
- Third page request – handled by service worker, responds with cached resources
So it’s only on the third page request that the cache kicks in.
A nice way to improve things is to use sw-toolbox
to pre-cache some resources. Pre-caching in this context means that any resources you specify will be cached when the service worker is first installed. Good targets for this are things like images, fonts, JavaScript, CSS: any resources that don’t change much. For example, the app shell, the HTML, CSS and JavaScript powering the app UI, is a good thing to pre-cache. This means we can have the cache kick in on the second request, instead of the third.
Let’s say you wanted to cache the homepage, and some resources, you could do so like this:
1 |
toolbox.precache(['index.html'],['logo.svg'],['main.css'],['main.js]']); |
This is a handy approach for caching items in advance of the user requesting them. This approach becomes even more powerful, if you can install the service worker before the user visits your page. We saw how this could work where an AMP page can install a service worker in a previous article.
Caching with service workers example
You can see this in action on a small example page at https://mobiforge.github.io/caching. The network tab of the Chrome developer tools is shown below, after the second page load. Note that it reports the various assets are delivered via the service worker: the app is cached!
One final point to note for the performance-obsessed among us who worry about the impact of including the sw-toolbox
library: it will add just 6.9KB; a pretty minimal, one-time hit with a big payoff. Definitely worth it!
You can find the full code example on github.
Leave a Reply