BrowserWorker - service workers made easy

anwar_nairi

Anwar

Posted on June 15, 2019

BrowserWorker - service workers made easy

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!

Let's go let's go! gif

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.

Image showing that the service worker is ready on the application panel of chrome

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!

But why? meme gif

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.

Service worker is not waiting the previous anymore and is installed correctly, viewing it in the application panel of the chrome console developper

Reload your browser and look at the network panel.

Home page have been handled by the service worker

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.

Cache storage contains the home page as well

Do you know what it means? Oh yes you know what it means!

Cat wiggling with Shaqil

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!

I have to go now meme gif

💖 💪 🙅 🚩
anwar_nairi
Anwar

Posted on June 15, 2019

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related