🤢

History

sw-toolbox & sw-precache

Main Issues of sw-*

  • Confusion how they worked together
  • sw-toolbox had a precache method
  • sw-toolbox's code was delicate
  • sw-precache was all or nothing
  • sw-precache was popular at the cost of flexibility
  • sw-precache did some *funky* lookup logic for precaching

Goals

  • Merge sw-precache's logic into a library
  • Put sw-precache and sw-toolbox under one roof
  • Break apart sw-toolbox into smaller modules so pieces can be used (or not used) based on user needs
  • Improve testing infra to allow future changes and growth

General Approach Taken v1

  • Source would be in ES Module syntax
    • Hopes of developers using Workbox this way
    • Hopes tree-shaking would be affective
  • Provide builds that can be used in the browser
  • Provide a low level and high level API
  • Merged
  • Testing was improved
  • New features (over toolbox)
    • BG Sync
    • Broadcast Cache Update
  • Simplified pre-caching logic

v1/v2

v1/v2

  • One Module Ruled
    • workbox-sw
  • Size was a big criticism (48KB)
  • Documentation was a mess
  • Webpack wasn't really webpack
  • Developer experience was still messy outside of the sw-precache style build step

v1 (Issues)

v1 (Issues)

// Where does this come from? Bare in mind version string.
importScripts('/workbox-sw.dev.v2.0.3.js');

const workboxSW = new WorkboxSW({
  // Options here can affect any module
  clientsClaim: true,
});

// `precache()` is defined on `workbox-sw` instead of
// on the `workbox-precaching` module
workboxSW.precache([{
  url: 'precached.txt',
  revision: '43011922c2aef5ed5ee3731b11d3c2cb',
}]);

workboxSW.router.registerRoute(
  'https://httpbin.org/image/(.*)',
  workboxSW.strategies.cacheFirst({
    // Theses options require specific plugins to exist in current context
    cacheName: 'images',
    cacheExpiration: {
      maxEntries: 2,
      maxAgeSeconds: 7 * 24 * 60 * 60,
    },
    cacheableResponse: {statuses: [0, 200]},
  })
);

v1 (Issues)

v3 (Beta)

workbox

.

.

<module>

*

v3 (Beta)

workbox

.

.

<module>

*

workbox-sw module

lightweight proxy object

[A convenience/loader]

v3 (Beta)

workbox

.

.

<module>

*

workbox-*

precaching

backgroundSync

etc

v3 (Beta)

workbox

.

.

<module>

*

  1. workbox <proxies> the module namespace
    1. If module needs loading:
      importScripts('<cdn or path>/<module name>.<dev or prod>.js')
    2. Return the module namespace

v3 (Beta)

workbox

.

.

<module>

*

importScripts('<CDN or Path>/workbox-sw.js');

workbox.precaching.*

via workbox-sw:

new Proxy(this, {
  get(target, key) {
    if (target[key]) {
      return target[key];
    }

    const moduleName = MODULE_KEY_TO_NAME_MAPPING[key];
    if (moduleName) {
      target.loadModule(moduleName);
    }

    return target[key];
  },
});

v3 (Beta)

workbox

.

.

<module>

*

without workbox-sw:

importScripts('<CDN or Path>/workbox-core.<dev or prod>.js');
importScripts('<CDN or Path>/workbox-precaching.<dev or prod>.js');

workbox.precaching.* <- Namespace from workbox-precaching

v3 (Beta)

  • Works with and without workbox-sw
    • The magic can be removed
  • We can switch between dev and prod easily
    • Localhost vs prod by default when loaded via workbox-sw
  • Forces clean module split
    • Config options
    • Access via Global namespace

v3 (Beta)

Browser Builds

importScripts('<CDN or Path>/workbox-sw.js');

workbox.precaching.*;
import precaching from 'workbox-precaching';

precaching.*;

ES Module

v3 (Beta)

import precaching from 'workbox-precaching';

precaching.*;

Source Code is ES Modules

{
  ..
  "main": "build/workbox-precaching.prod.js",
  "module": "index.mjs",
  ..
}

Main and Module in package.json

v3 (Beta)

Browser Builds are Enforced Rules + Rollup

  1. All workbox modules are treated as external
    1. Default for Node, but easy to skip with monorepo
    2. External modules MUST reference a specific file and export (Helps tree shake)
  2. Rollup treats external modules as "globals" and swaps reference for `workbox.<module>`
// The implementation of workbox-foo
import {logger} from 'workbox-core/_private/logger.mjs';

export default function() {
  logger.log('hello');
}
this.workbox = this.workbox || {};
this.workbox.foo = (function(logger_1) {
  return function() {
    logger_1.log('hello');
  };
})(workbox.core._private.logger)

v3 (Beta)

Input -> Output

v3 (Beta)

Import / Export Consistency

  • Every module must have a default export
    • Enables same API for ES Modules and browser
  • Importing from a workbox-* module must be either a `public` or `private` export
    • A simple rule to enforce and validate with tests
    • A simple rule to ensure between ES Modules and browser builds

v3 (Beta)

CLI

(NPM) build

webpack

v3 (Beta)

webpack

 

Using the correct events and supporting chunks

 

More community PR's since improving

v3 (Beta)

Inject Manifest

 

Find `precacheAndRoute([])` and inject a list of files and revisions.

OR

Inject `importScripts('precache-1234.js')` which exposes `self.__precacheManifest`.

 

Good for people wanting their own SW, but not intuitive.

v3 (Beta)

Generate SW

 

Same as sw-precache with option of use CDN or local Workbox builds

 

Hard to get away from this once you've started.

Configuration is growing and duplication Config -> Templated Code.

v3 (Beta)

  • Modules are seperate (No single module)
  • Size is 22.7KB vs 48KB
    • This assumes using everything
  • Documentation is cleaner
  • Webpack is cleaner
  • Developer experience will hopefully improve*

*Maybe I made things worse

v3 (Beta)

v3 (Beta)

// Version is directory name, can be CDN or local
importScripts('<CDN or Local>/<version>/workbox-sw.js');

// Options in respective places
workbox.clientsClaim();
workbox.core.setLogLevel(workbox.core.LOG_LEVEL.debug);

// All methods under module namespace
workboxSW.precaching.precacheAndRoute([{
  url: 'precached.txt',
  revision: '43011922c2aef5ed5ee3731b11d3c2cb',
}]);

workboxSW.router.registerRoute(
  'https://httpbin.org/image/(.*)',
  workboxSW.strategies.cacheFirst({
    cacheName: 'images',
    // Plugins are explicit
    plugins: [
        workbox.cacheExpiration.plugin({
            maxEntries: 2,
            maxAgeSeconds: 7 * 24 * 60 * 60,
        }),
        workbox.cacheableResponse.plugin({
          statuses: [0, 200],
        }),
    ],
  })
);

Downsides of Workbox

  • Still rough edges around API
  • Small set of maintainers / committee
  • Docs
  • SW are still hard
  • Build is pretty crazy

Workbox V3 Deck

By gauntface

Workbox V3 Deck

  • 1,226