Setup PWA With SWPrecacheWebpackPlugin

February 7, 2018

SWPrecacheWebpackPlugin is a webpack plugin for using service workers to cache your external project dependencies. It will generate a service worker file using sw-precache and add it to your build directory.

This Webpack plugin generates a service worker using sw-precache that will cache webpack’s bundles’ emitted assets. You can optionally pass sw-precache configuration options to webpack through this plugin.

This article assume you have basic knowledge of PWA and Webpack.

NOTE: It is recommended to read about basic PWA setup to know about PWA requirements, Web App Manifest, Service Worker and Lighthouse.

Install & Configure SWPrecacheWebpackPlugin

Install sw-precache-webpack-plugin.

npm install --save-dev sw-precache-webpack-plugin

Edit your webpack configuraion file.

const SWPrecacheWebpackPlugin = require('sw-precache-webpack-plugin')

module.exports = {
  ...
  plugins: [
    ...
    // service worker caching
    new SWPrecacheWebpackPlugin({
      cacheId: 'myapp',
      filename: 'service-worker.js',
      staticFileGlobs: ['dist/**/*.{js,css}', '/'],
      minify: true,
      stripPrefix: 'dist/',
      dontCacheBustUrlsMatching: /\.\w{6}\./
    })
  ],
  ...
}

I am using vue-webpack-boilerplate, so the service worker will be generated at dist/service-worker.js. Looking inside this file you shall see the assets which will be cached by service worker.

...

var precacheConfig = [
  ["service-worker.js","d3713395f64534beb43d3a80883762d3"],
  ["static/css/app.css","eb9f16dfeabee33d2974c202d1740c9b"],
  ["static/css/vendor.css","da2c94e8757b06335e628b0030e00591"],
  ["static/js/app.js","626cf27bbc683df3db780650b1f6c00f"],
  ["static/js/manifest.js","81bb8bd7f534ad7862c3b8a0fd2ded9e"],
  ["static/js/sw.js","fe5fe7029d8b78fe22649e55c8a931c3"],
  ["static/js/vendor.js","db2ab78ce43156b92a611c7dc478fb27"]];

...

NOTE: if the path generated (static/ for my case) is incorrect, you can use stripPrefixMulti to do a find & replace operation.

new SWPrecacheWebpackPlugin({
  ...
  stripPrefixMulti: {
    'dist/static/': '/static/pack/',
    'dist/': ''
  },
})

NOTE: if you want to cache urls which is not part of this webpack (e.g /, /about, /data.json, etc.), you can use dynamicUrlToDependencies

new SWPrecacheWebpackPlugin({
  ...
  // specify external url to cache: whenever the url content changed/updated, change v1 to another value to indicate changes
  // other mechanism is available as well, read the docs
  dynamicUrlToDependencies: {
    '/': 'v1'
  },
})

NOTE: if you want the service worker to include other features (e.g. push notification) besides caching, you can add extra script logic using importSripts. In this case, two javascripts shall be fetched for service worker. You can also use templateFilePath to modify sw-precache template file to include your logic, but this method is more complicated. You can look into this and this as well.

importScripts: [
  // assume this js file is written manually outside of webpack
  { filename: '/static/js/sw-push-notification.js' }
})

or

module.exports = {
  entry: {
    app: './src/main.js',
    sw: './src/sw.js'
  },
  output: {
    path: path.resolve(__dirname, '../dist'),
    filename: '[name].js',
    publicPath: '/'
  },
  ...
  plugins: [
    ...
    // service worker caching
    new SWPrecacheWebpackPlugin({
      ...
      importScripts: [
        // use js file generated by webpack
        { chunkName: 'sw' }
      ]
    })
  ],
  ...
}

NOTE: if you bump into webpackjsonp is not defined when webpage trying to include sw.js, make sure manifest.js (or you main webpack js) is included before sw.js.

Register Service Worker

After service-worker.js is generated, you want to register it. I prefer to embed the javascript code in html (rather than include another javascript for registration).

(function() {
  if('serviceWorker' in navigator) {
    navigator.serviceWorker.register('/service-worker.js');
  }
})();

NOTE: refer to service worker registration code from sw-cache or vuejs-templates/pwa.

NOTE: it’s best to put your /service-worker.js at root path due to scope. If you use /static/js/service-worker.js, it won’t work when your current page path is /.

NOTE: You probably want to configure your webserver not to cache the service-worker.js file (refer to this and this).

Below is a sample nginx configuration for no caching.

location /service-worker.js {
  alias /myapp/static/js/service-worker.js;
  expires -1;
}

Use Chrome DevTools

Your sw-precache-webpack-plugin configuration should be complete now (assuming you already configured the web app manifest). Use Chrome DevTools and Lighthouse to verify if everything is running properly.

  • Chrome DevTools -> Application, check Application/Service Workers and Cache/Cache Storage.
  • Chrome DevTools -> Audits -> Perform an audit -> Progressive Web App -> Run audit

NOTE: you might want to look into Workbox by Google or Service Worker Toolchain by Pinterest.

References:

This work is licensed under a
Creative Commons Attribution-NonCommercial 4.0 International License.