codamit.dev

Runtime cache made easy

January 24, 2019

Service workers improve client-side performance but they are not easy to build. They bring complexity in the build pipeline and they have a huge impact on what’s going on in the browser.

How can we simplify the service worker creation ?

Simply by not creating them. Nowadays we’re all using tools like Webpack that generates a bundle associated with a hash. This hash is recreated each time source code is changed.

Version: webpack 4.29.5
Time: 5435ms
Built at: 2019-03-03 17:06:12

app-admin.f749dac1d8ea877edf08.js       69.5 KiB    [emitted]  
app-article.5b996e3b9cffbefbc618.js     115 KiB     [emitted]  
app-error.892811b3974653ad852f.js       23.3 KiB    [emitted]  
app-home.67b6eecbf2b49d55ed66.js        49.9 KiB    [emitted]  
app-login.a7ac57a5bcc4588af0a1.js       22.7 KiB    [emitted]  

Keep in mind that to precache resources in a service worker we need to hard-code their public path. So each time we’re bundling a new version of our application we should update the service worker :

  • point these new resources in pre-cache configuration using the public path
  • increment the cache version to notify the browser to refresh the cache

sw-precache to the rescue

Google created a library that exactly solve our problem. So let’s get it.

$ npm i sw-precache --save-dev

Then we can create a configuration file.

module.exports = {
  staticFileGlobs: ['dist/**/*.{js,html,css,png,jpg,gif,svg,eot,ttf,woff}'],
  runtimeCaching: [
    {
      urlPattern: /^https:\/\/codamit\/.com/,
      handler: "fastest",
    },
  ],
};

Here we want to precache all static resources located in the dist folder and serve them cache-first. This strategy is a huge gain in therms of performance since our application is entirely powered by JavaScript.

Traditionally the browser needs to fetch those resources through the network, parse them and execute them before user is getting his first meaningful paint. With our configuration the browser will skip the network step and it feels like the application is booting instantly.

We need to generate our service worker at build time. To do that we’re using the sw-precache command line interface in an npm script.

{
  "scripts": {
    "build:client": "webpack ---progress --config=webpack.production.config.js --mode production",
    "build:sw": "sw-precache --root=dist --config=src/client/sw-config.js",
    "postbuild:client": "npm run build:sw"
  }
}

Now when I build the application using the npm run build:client command the service worker is generated in the dist folder and the corresponding file begin with an array with our static resources.

var precacheConfig = [["12.bdac597b51c5a0181cb7.js", "b019a86ed940d00c5addea643938fe96"], ["13.834a6c32791d7f770b2e.js", "8a148e8566e9fc75132281d073c1b005"], ["app-admin.d3a49e109297d25509ec.js", "dbd4b4318b4b27d0a4965395c515928c"], ["app-article.f0f074c1d52252578202.js", "c2e29022fd8e616a1640a1715d19c68b"], ["app-articles-by-tag.ca1b9eb12300758ba2a7.js", "d79c3e5bf0c95ef3ee31a417a4805088"], ["app-error.f1bc73a5bc094268f774.js", "f39e1fb8f794c350c43fbd8f642bac8a"], ["app-home.ae774d639d8b71889ed4.js", "73959e6c3804c234a7fc99412d94f44b"], ["app-login.079c3983529100517325.js", "ffcc7d91aac4f4e6d7d302b5e7d80707"], ["main.d6499075ebf858f54463.js", "fdc4d61896e2054597a32d13b22d9aec"], ["vendor.09af45d7553663427450.js", "39f9b90230a8254776dc81c12fc2016a"], ["vendors~app-admin~app-article~app-articles-by-tag~app-home.2ae4c9523050bd11a6c7.js", "acb5345b6dcccde3d2288f797d40541c"], ["vendors~app-article.3b72d27ddecb1a8721ee.js", "e9d5ae538fdeb47a2c0501cc5b35744a"], ["vendors~app-home.c34d5f552da95a4de43c.js", "25bd173a8ae31fa26834e97cad001d98"]];
var cacheName = 'sw-precache-v3-sw-precache-' + (self.registration ? self.registration.scope : '');

It means that our service worker detects all static resources and automatically keep those resources up to date when changes are detected in builds.

Service worker cache storage

It’s simple as is.

I'm Edouard Bozon, I play almost everyday with web technologies. I focus my work on building better JavaScript apps and contributing to open source. I'm freelancer, currently working on awesome projects.