Espen Henriksen

Front-end team lead

Oslo Market Solutions

 

espen_dev

Designing for performance with Service Workers

What are service workers?

In a nutshell

  • An event driven worker
  • Served as a separate JavaScript-file
  • Has special APIs
  • Acts as a programmable caching proxy inside the browser
  • Will control a page and all subsequent visits
  • Runs as a separate thread which will keep running even when the browser tab is closed

Lifecycle

  • Progresses through the lifecycle until it reaches "activated"
  • Only one service worker can be "activated" for each website
  • Once a newer worker is activated, the old one is made redundant and discarded
  • Waits until existing worker closes unless told to skip waiting

Caching

  • Intercepts all requests
  • Can serve from cache or send the request to the network
  • Can be programmed to one of many caching strategies
  • Precaching
    • Cache all static assets in the install step
    • workbox-webpack-plugin

Precaching

  • Preloads all static assets into the cache in the service worker's install stage
  • JS, CSS, fonts, etc..
  • Uses browser idle time
  • Ensures all subsequent navigation is fast

Precaching

Precaching

All requests are fetched from cache.

The "Cache only" strategy

Network falling back to cache

  • Always fetch from network
  • Update cache when response arrives
  • Use cache when network is down
  • Quick fix for getting offline support

Network falling back to cache

Cache then network

  • Like network-then-cache, but faster
  • Show cached data first
  • Update view with fresh data when response returns
  • Best of both worlds, fast and supports offline

Cache then network

Still awake?

Design Patterns

PRPL

Push

  • Push critical resources for the initial URL route.
  • Fonts, CSS, JS chunks
  • HTTP/2
  • "But HTTP/2 server push is not cache aware!"
  • index.html is only served once, during service worker install

Render

  • Render initial route
  • App shell architecture
  • User sees "shell" to indicate that loading is in progress
  • View loads when it is ready

Pre-cache

  • Pre-cache remaining routes
  • Service worker boots up
  • Installs all static assets into cache

Lazy-load

  • Lazy-load and create remaining routes on demand
  • When user navigates, pull routes from cache
  • Fast response to user actions

Push
Render
Pre-cache

Lazy-load

Developing service workers

Workbox

  • A library for building service workers programatically
  • workbox-webpack-plugin
  • Automatically precaches webpack chunks

Workbox example

// Inside of webpack.config.js:
const WorkboxPlugin = require('workbox-webpack-plugin');

module.exports = {
  // Other webpack config...

  plugins: [
    // Other plugins...

    new WorkboxPlugin.GenerateSW()
  ]
};

Workbox example with runtime caching

// Inside of webpack.config.js:
const WorkboxPlugin = require('workbox-webpack-plugin');

module.exports = {
  plugins: [
    new WorkboxPlugin.GenerateSW({
      // Define runtime caching rules.
      runtimeCaching: [
        {
          // Match any request ends with .png, .jpg, .jpeg or .svg.
          urlPattern: /\.(?:png|jpg|jpeg|svg)$/,

          // Apply a cache-first strategy.
          handler: 'cacheFirst',

          options: {
            // Only cache 10 images.
            expiration: {
              maxEntries: 10,
            },
          },
        },
      ],
    })
  ]
};

Finally, register the worker

if ('serviceWorker' in navigator) {
  navigator.serviceWorker.register('/service-worker.js');
  // Register returns a promise
}

Devtools demo

Pitfalls

Security limitations

The worker file

  • Must not be cached
  • An earlier version of the spec allowed it to be cached up to 24 hours
  • Now all browsers should treat it as no-cache
  • Immutable filename
  • (No cache key in name)

Deploys

  • Changes the loading paradigm we know
  • New service worker discovered after load
  • Client will run stale code after deploy
  • We can auto-refresh the browser on upgrade
  • Better solution: Inform user of a new version and nag them to refresh

Rollback

  • You can't simply delete service worker if things break
  • Only way to replace running workers is by deploying a "no-op" worker

Thanks for listening!

Questions?

Designing for performance with Service Workers

By Eline H

Designing for performance with Service Workers

  • 32