BrowserWorker - service workers made easy
Anwar
Posted on June 15, 2019
Welcome everyone!
I am very happy to show you BrowserWorker, my latest project. The goal of this library is to help you set up a Service Workers more easily than having to deal with a lot of verbose syntax or copy/paste to use a specific cache strategy.
At the moment I am writing this post, the library is in early development stage, and only usable through WebPack, Browserify or any other tool that transpiles your code for the browser. In future releases, you will be able to just use it from a CDN.
If you like the idea of this library, let's see how to use it!
Installing
In a fresh new folder, initialize a node project. For the rest of the topic, I will use yarn
, but you can use any dependency manager you like.
yarn init --yes
I use --yes
to skip filling the configuration.
Then, we will install @khalyomede/browser-worker
.
yarn add --dev @khalyomede/browser-worker
The rest of the tools I will install is to be able to create a browser-compatible code. If you already use a tool like WebPack or Rollup, it is fine to skip this and jump into the next section.
To continue, I will add gulp
, gulp-bro
, (browserify), gulp-plumber
(to not terminate my build at the first error), @babel/core
, @babel/preset-env
(to transpile ES6 syntaxes), @babel/register
(to be able to use ES6 in my gulpfile.js
), babelify
(to pass it to gulp-bro
), and browser-sync
(to reload our page when files have been changed and saved).
yarn add --dev gulp gulp-bro gulp-plumber @babel/core @babel/preset-env @babel/register babelify
Then, I will create a gulpfile.babel.js
to use gulp recipes in order to produce a browser-compatible code.
// your-app/gulpfile.babel.js
import { src, dest, watch, series } from "gulp";
import browserify from "gulp-bro";
import plumber from "gulp-plumber";
import browserSync from "browser-sync";
const browser = browserSync.create();
const js = () =>
src("src/**/*.js")
.pipe(plumber())
.pipe(browserify({ transform: ["babelify"] }))
.pipe(dest("dist"))
.pipe(browser.stream());
const reload = done => {
browser.reload();
done();
};
const start = () => {
browser.init({
server: {
baseDir: "dist"
}
});
watch("src/**/*.js", series(js, reload));
};
export { start };
I need to update my package.json
to be able to use a start
command, which will call gulp watch
and transpile my files when they change.
...
"scripts": {
"start": "gulp start"
}
...
I will also need a .babelrc
file to set up my preset:
{
"presets": ["@babel/preset-env"]
}
We are good to go! You can run the start command now.
yarn start
You will see that your browser will open and the page will display a 404. This is totally fine and we will create the html file soon.
Set up
In a first place, we will create the service worker itself, and then creating the script that will register it.
In the file src/service-worker.js
, add this code.
// your-app/src/service-worker.js
import { BrowserWorker } from "@khalyomede/browser-worker";
BrowserWorker.enableDebug();
On save, you should see that a new file dist/service-worker.js
have been generated. Do not dare to see it, young fool! It has been made from dark magic and young internship python developpers blood.
This code above will be run by the service worker, but it cannot run until we register it.
In another file, register-service-worker.js
, add this code.
// your-app/src/js/register-service-worker.js
import { BrowserWorker } from "@khalyomede/browser-worker";
BrowserWorker.enableDebug();
BrowserWorker.setServiceWorkerPath("/service-worker.js").registerServiceWorker();
Again, saving this file should create its dist counterpart file.
This code is responsible for registering your service worker on your page.
Finally, to see the result of this setup, we will create an HTML file that will reference our register-service-worker.js
file. Create it on the dist file (it is dirty, but fine for this showcase).
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>Document</title>
<script type="text/javascript" src="/js/register-service-worker.js" async></script>
</head>
<body>
<h1>Hello from the index HTML</h1>
</body>
</html>
Now you can come back to your browser, and reload the page http://localhost:3000
to see the page.
Open the developper console, and you should see the following message:
[BrowserWorker][15:51:43.598] service worker registered (scope: http://localhost:3000/).
If you reload your page, you will see that your service worker is now online.
Since your are on the Application
panel (on Chrome), try to check the Offline
option and reload your page... Yes, it totally screwed your app and a wild dinosaur appears!
Setting up a cache strategy
Since you did not tell your service worker to process requests, nothing happens. Let's fix this by using a network first strategy on our home page.
Update your service-worker.js
file like this.
import { BrowserWorker, CacheStrategy } from "@khalyomede/browser-worker";
BrowserWorker.enableDebug();
BrowserWorker.setCacheStrategy(CacheStrategy.NETWORK_FIRST)
.setCacheName("network-first-v1")
.addRoute("/");
BrowserWorker.listenRequests();
Now, after saving your file, you should see something like this in console.
[BrowserWorker][16:05:26.652] service worker registered (scope: http://localhost:3000/).
[BrowserWorker][16:05:28.457] waiting for others instances before installing (if you want to skip waiting, use BrowserWorker.disableWaitingOtherInstances())
And in your Application panel, you see that your new service worker is waiting for the other to terminate to install ("Waiting to activate"). To fix this, add this line right after enabling the debug mode.
import { BrowserWorker, CacheStrategy } from "@khalyomede/browser-worker";
BrowserWorker.enableDebug();
BrowserWorker.disableWaitingOtherInstances(); // this one
BrowserWorker.setCacheStrategy(CacheStrategy.NETWORK_FIRST)
.setCacheName("network-first-v1")
.addRoute("/");
BrowserWorker.listenRequests();
Now you should see better results in your console:
[BrowserWorker][16:08:38.669] service worker registered (scope: http://localhost:3000/).
[BrowserWorker][16:08:40.637] skipped waiting for other instances to finish.
[BrowserWorker][16:08:41.653] cleaned old caches.
And in your Application panel, no more service worker waiting to install.
Reload your browser and look at the network panel.
Your home page seems to have been handled by your service worker, congrats! Since you asked your service worker to fetch it from the network first, it has also nicely put your response in the cache.
Go on the Application panel again, and check for the "Cache" section.
Do you know what it means? Oh yes you know what it means!
Check the "Offline" checkbox again and reload the page: offline web app!
Conclusion
With this first introduction on how to use BrowserWorker, we have successfuly used a cache strategy to respond to both online and offline network condition.
Service workers are becoming mature and that is why I wanted to create this library to help you use it in your app.
Feel free to create issues if you noticed some. Some parts of this library remain untested, so be careful in the jungle.
Last but not least, make sure you read the Advice
section of the README, it contains precious information even if you want to try Service Worker on your own without this library.
If you want to use more in this library, like to be able to cache a resource ahead of time to recreate "read offline later" features, and so on, make sure to check the README on the documentation : https://www.npmjs.com/package/@khalyomede/browser-worker
I hope you had fun like I had, learnt things, and maybe want to try this technology for your app.
Happy caching!
Posted on June 15, 2019
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.