If you find yourself regularly writing code in your Service Workers that caches resources, you might find Workbox (https://developers.google.com/web/tools/workbox/) helpful. Written by one of the teams at Google, itβs a library of helpers to get you started creating your own Service Workers in no time, with built-in handlers to cover the most common network strategies. In a few lines of code, you can decide whether you want to serve specific resources solely from the cache, serve resources from the cache and then fall back, or perhaps only return resources from the network and never cache. This library gives you total control over your caching strategy.
Workbox provides you with a quick and easy way to reuse common network caching strategies instead of rewriting them again and again. For example, say you wanted to ensure that you always retrieve your CSS files from the cache but only fall back to the network if a resource wasn't available. Using Workbox, you register your Service Worker the same way you have in the Introduction to Service Workers code lab. Then you import the library into your Service Worker file and start defining routes that you want to cache.
workbox-cliIn this code lab, we are building on top of the project started in the Introduction to Service Workers code lab.
If you didn't do it already: Fork and then Clone the following repository: https://github.com/The-Guide/fe-guild-2019-pwa.git
$ git clone https://github.com/[YOUR GITHUB PROFILE]/fe-guild-2019-pwa.git
$ cd fe-guild-2019-pwa
If you want to start directly with Workbox checkout the following branch:
$ git checkout pwa-workbox-initFirst install the dependencies
$ npm installThen type in the terminal
$ npm startand open Chrome at localhost:8080fe-guild-2019-pwa/
Navigate to the project directory and run from the command line:
$ npm install workbox-cli --save-devFor some Windows version, it can hang so install the latest beta instead
$ npm install workbox-cli@beta --save-devworkbox-cli is a command-line tool that lets us configure, generate, and modify service worker files. You'll learn more about this in a later step.
workbox-cli can be run from command line using npx that ships with newer versions of Node and NPM
$ npx workboxIf npx is not present you can install workbox-cli globally (with the --global flag)
$ npm install --global workbox-cliIn sw.js
importScripts('https://storage.googleapis.com/workbox-cdn/releases/3.5.0/workbox-sw.js');
if (workbox) {
console.log(`Yay! Workbox is loaded π`);
workbox.precaching.precacheAndRoute([]);
} else {
console.log(`Boo! Workbox didn't load π¬`);
}
The importScripts call imports the workbox-sw.js library from a Content Delivery Network (CDN). Once the library is loaded, the workbox object gives our service worker access to all the Workbox modules.
The precacheAndRoute method of the precaching module takes a precache "manifest" (a list of file URLs with "revision hashes") to cache on service worker installation. It also sets up a cache-first strategy for the specified resources, serving them from the cache by default.
Currently, the array is empty, so no files will be cached.
Rather than adding files to the list manually like with did in the previous code lab, workbox-cli can generate the manifest for us. Using a tool like workbox-cli has multiple advantages:
workbox-cli to our build process eliminates the need for manual updates to the precache manifest each time that we update the app's files.workbox-cli automatically adds "revision hashes" to the files in the manifest entries. The revision hashes enable Workbox to intelligently track when files have been modified or are outdated and automatically keep caches up to date with the latest file versions. Workbox can also remove cached files that are no longer in the manifest, keeping the amount of data stored on a user's device to a minimum. You'll see what workbox-cli and the file revision hashes look like in the next section.The first step towards injecting a precache manifest into the service worker is configuring which files we want to precache. In this step, we create the workbox-cli configuration file.
From the project directory, run the following command:
$ npx workbox wizard --injectManifestNext, follow the command-line prompts as described below:
root of the app. The root specifies the path where Workbox can find the files to cache. For this lab, the root is the fe-guild-2019-pwa/ directory, which should be suggested by the prompt. You can either type "fe-guild-2019-pwa/" or choose "fe-guild-2019-pwa/" from the list.Once you've completed the prompts, you'll see a log with instructions for building the service worker. Ignore that for now (if you already tried it, that's okay). We will explore in the next step.
Let's examine the newly created workbox-config.js file.
module.exports = {
"globDirectory": "fe-guild-2019-pwa/",
"globPatterns": [
"**/*.{html,ico,json,css,js}",
"src/images/*.{jpg,png}"
],
"swDest": "fe-guild-2019-pwa/sw.js",
"swSrc": "fe-guild-2019-pwa/sw-template.js",
"globIgnores": [
"../workbox-config.js",
"sw-template.js",
"help/**"
]
};
Workbox creates a configuration file (in this case workbox-config.js) that workbox-cli uses to generate service workers. The config file specifies where to look for files (globDirectory), which files to precache (globPatterns), and the file names for our source and production service workers (swSrc and swDest, respectively). We can also modify this config file directly to change what files are precached. We explore that in the later step.
Now let's use the workbox-cli tool to inject the precache manifest into the service worker.
Open package.json and create a build script to run the Workbox injectManifest command. The updated package.json should look like the following:
"scripts": {
"start": "http-server -c0",
"build": "workbox injectManifest workbox-config.js",
"deploy": "npm run build && gh-pages -d fe-guild-2019-pwa"
}
Save the file and run npm run build from the command line.
The precacheAndRoute call in sw.js has been updated. In your text editor, open sw.js and observe that all the CSS files are included in the file manifest.
Return to the app in your browser (http://localhost:8080/fe-guild-2019-pwa/). Open your browser's developer tools (in Chrome use Ctrl+Shift+I on Windows, Cmd+Opt+I on Mac). Unregister the previous service worker and clear all service worker caches for localhost so that we can test our new service worker. In Chrome DevTools, you can do this in one easy operation by going to the Application tab, clicking Clear Storage and then clicking the Clear site data button.
Refresh the page and check that a new service worker was installed. You can see your service workers in Chrome DevTools by clicking on Service Workers in the Application tab. Check the cache and observe that all CSS files are stored. In Chrome DevTools, you can see your caches by clicking on Cache Storage in the Application tab.
When workbox injectManifest is called, Workbox makes a copy of the source service worker file (fe-guild-2019-pwa/sw-template.js) and injects a manifest into it, creating our production service worker file (fe-guild-2019-pwa/sw.js). Because we configured workbox-config.js to cache *.css files, our production service worker has all the CSS files in the manifest. As a result, all the CSS files were pre-cached during the service worker installation.
Now whenever we update our app, we can simply run npm run build to update the service worker.
module.exports = {
"globDirectory": "fe-guild-2019-pwa/",
"globPatterns": [
"**/*.{html,ico,json,css,js}",
"src/images/*.{jpg,png}"
],
"swDest": "fe-guild-2019-pwa/sw.js",
"swSrc": "fe-guild-2019-pwa/sw-template.js",
"globIgnores": [
"../workbox-config.js",
"sw-template.js",
"help/**"
]
};
From the project directory, re-run npm run build to update sw.js. The precache manifest in the production service worker (sw.js) has been updated to contain all the static files we had previously.
Refresh the app and activate the updated service worker in the browser. In Chrome DevTools, you can activate the new service worker by going to the Application tab, clicking Service Workers and then clicking skipWaiting. Observe in developer tools that the globPatterns files are now in the cache (you might need to refresh the cache to see the new additions).
If you enable Offline mode from the Network tab the home page loads but not the fonts, we will fix that in the next step.
By editing the globPatterns files in workbox-config.js, we can easily update the manifest and precached files. Re-running the workbox injectManifest command (via npm run build) updates our production service worker with the new configuration.
In addition to precaching, the precacheAndRoute method sets up an implicit cache-first handler. This is why the home page loaded while we were offline even though we had not written a fetch handler for those files!
workbox-sw.js has a routing module that lets you easily add routes to your service worker.
Let's fix the fonts problem now. Copy the following code into sw-template.js beneath the precacheAndRoute call. Make sure you're not editing the production service worker, sw.js, as this file will be overwritten when we run workbox injectManifest again.
In sw-template.js
workbox.routing.registerRoute(
/.*(?:googleapis|gstatic)\.com.*$/,
workbox.strategies.staleWhileRevalidate({
cacheName: 'google-fonts',
plugins: [
new workbox.expiration.Plugin({
maxEntries: 3,
maxAgeSeconds: 30 * 24 * 60 * 60 // 30 Days
})
]
}));
Save the file
Restart the server and rebuild the app and service worker with the following commands:
$ npm run build
$ npm run start
Refresh the app and activate the updated service worker in the browser. Put the app offline and check that the Google Fonts load correctly now. You may need to refresh twice.
In this code, we added a route to the service worker using the registerRoute method on the routing class. The first parameter in registerRoute is a regular expression URL pattern to match requests against. The second parameter is the handler that provides a response if the route matches. In this case, the route uses the strategies class to access the staleWhileRevalidate run-time caching strategy. Whenever the app requests the Google Fonts, the service worker checks the cache first for the resource before going to the network.
The handler in this code also configures Workbox to maintain a maximum of 3 entries in the cache. Once 3 entries have been reached, Workbox will remove the oldest one automatically. The fonts are also set to expire after 30 days, signaling to the service worker that the network should be used for those entries.
In this last step we will bring the service worker to the same level we left it in the Introduction to Service Workers code lab. For that we will register a new route with a custom handler
workbox.routing.registerRoute(
routeData => routeData.event.request.headers.get('accept').includes('text/html'),
args => {
return caches.match(args.event.request)
.then(response => {
if (response) {
console.log(response);
return response;
}
// Clone the request - a request is a stream and can be only consumed once
const requestToCache = args.event.request.clone();
// Try to make the original HTTP request as intended
return fetch(requestToCache)
.then(response => {
// If request fails or server responds with an error code, return that error immediately
if (!response || response.status !== 200) {
return response;
}
// Again clone the response because you need to add it into the cache and because it's used
// for the final return response
const responseToCache = response.clone();
caches.open('dynamic')
.then(cache => {
cache.put(requestToCache, responseToCache);
});
return response;
});
})
.catch(error => {
return caches.match('/fe-guild-2019-pwa/offline.html');
});
}
);
fetch callback from the Introduction to Service Workers code lab. See if you spot any differences/images/icons/* and /images/splashscreens/* and handle the request/response using the staleWhileRevalidate strategy. Name the caches and think about values for the expiration plugin.GitHub Pages and test again.You have learned how to use Workbox to create production-ready service workers!
workbox-cli$ git checkout pwa-workbox-final