事前キャッシュなしでのワークボックスの使用

これまで、このドキュメントではプレキャッシュに重点を置き、ビルドツール generateSWinjectManifest に重点的に触れてきました。Service Worker にプレキャッシュ ロジックを含めるべき理由はたくさんありますが、Workbox を使用するのにプレキャッシュを使用する必要はありません。

プロジェクトでランタイム キャッシュのみが必要な場合や、Web push など、よりクリーンな方法で Service Worker API を統合したい場合があります。この記事では、Workbox のビルドツールが不要なケースについて説明します。

バンドラを使用する場合

Bundler はウェブ開発の分野では目覚ましく、お客様のプロジェクトで使われている可能性が高いと言えます。この場合、何も事前キャッシュしない場合はバンドラ プラグイン(workbox-webpack-plugin など)を使用する必要はありません。Service Worker は、アプリケーションの別のエントリ ポイントとして扱われます。

プロジェクトのソース ディレクトリのルートに Service Worker を作成し、アプリケーションに必要な Workbox モジュールを使用します。次の例では、ナビゲーションと画像アセット リクエストのキャッシュ戦略を別々の Cache インスタンスで設定する、プレキャッシュを使用しない例を示します。

// sw.js
import {NetworkFirst, CacheFirst} from 'workbox-strategies';
import {registerRoute, NavigationRoute, Route} from 'workbox-routing';

const navigationRoute = new NavigationRoute(new NetworkFirst({
  cacheName: 'navigations'
}));

const imageAssetRoute = new Route(({request}) => {
  return request.destination === 'image';
}, new CacheFirst({
  cacheName: 'image-assets'
}));

registerRoute(navigationRoute);
registerRoute(imageAssetRoute);

ここからは、選択したバンドラのエントリ ポイントとして、この Service Worker を指定するだけです。以下に、いくつかの一般的なバンドラでこれを行う方法の例を示します。

Webpack

webpack は、entry 構成でエントリ ポイントを受け入れます。この方法を使用する場合は、次の点に注意してください。

  1. Service Worker のスコープを可能な限り広くするには、出力ディレクトリのルートに出力されるようにする必要があります。
  2. Service Worker を更新すると新しいハッシュが生成され、ウェブサイトに複数の Service Worker がデプロイされる可能性があるため、Service Worker のバージョニングはおすすめしません。

上記の条件を満たすために、関数を output.filename に渡すことができます。この関数は、処理中の現在のエントリ ポイントが Service Worker のエントリ ポイントであるかどうかを確認します。それ以外の場合、バージョニングされたファイルは通常の宛先に書き込まれます。

// webpack.config.js
import process from 'process';

const isProd = process.env.NODE_ENV === 'production';

export default {
  mode: isProd ? 'production' : 'development',
  context: process.cwd(),
  entry: {
    // Service worker entry point:
    sw: './src/sw.js',
    // Application entry point:
    app: './src/index.js'
  },
  output: {
    filename: ({runtime}) => {
      // Check if the current filename is for the service worker:
      if (runtime === 'sw') {
        // Output a service worker in the root of the dist directory
        // Also, ensure the output file name doesn't have a hash in it
        return '[name].js';
      }

      // Otherwise, output files as normal
      return 'js/[name].[contenthash:8].js';
    },
    path: './dist',
    publicPath: '/',
    clean: true
  }
};

ロールアップ

Rollup も webpack と似ています。ただし、複数のエントリ ポイントが、配列でエクスポートされる個別の構成オブジェクトとして指定されます。

// rollup.config.js
import { nodeResolve } from '@rollup/plugin-node-resolve';
import replace from '@rollup/plugin-replace';

// Plugins common to both entry points
const plugins = [
  nodeResolve(),
];

export default [
  // Application entry point
  {
    input: './src/index.js',
    output: {
      dir: './dist/js',
      format: 'esm'
    },
    plugins
  },
  // Service worker entry point
  {
    input: './src/sw.js',
    output: {
      file: './dist/sw.js',
      format: 'iife'
    },
    plugins: [
      ...plugins,
      // This @rollup/plugin-replace instance replaces process.env.NODE_ENV
      // statements in the Workbox libraries to match your current environment.
      // This changes whether logging is enabled ('development') or disabled ('production').
      replace({
        'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV || 'production')
      })
    ]
  }
];

ESbuild

esbuild には、わかりやすいコマンドライン インターフェースが用意されています。

npx esbuild ./src/sw.js --bundle --minify --outfile=./dist/sw.js

esbuild は、process.env.NODE_ENV をデフォルトで「development」または圧縮が有効になっている場合は「production」に置き換えます。

workbox-sw を使用してバンドラを使用しない場合

プロジェクトでバンドラを使用しない場合もあります。workbox-sw は、Service Worker 内の CDN から Workbox ランタイムを読み込むことができます。importScripts でインポートした場合は、ビルドステップは必要ありません。

// sw.js

// Imports Workbox from the CDN. Note that "6.2.0" of the URL
// is the version of the Workbox runtime.
importScripts('https://storage.googleapis.com/workbox-cdn/releases/6.2.0/workbox-sw.js');

const navigationRoute = new workbox.routing.NavigationRoute(new workbox.strategies.NetworkFirst({
  cacheName: 'navigations'
}));

const imageAssetRoute = new workbox.routing.Route(({request}) => {
  return request.destination === 'image';
}, new workbox.strategies.CacheFirst({
  cacheName: 'image-assets'
}));

workbox.routing.registerRoute(navigationRoute);
workbox.routing.registerRoute(staticAssetRoute);

CDN から Workbox ランタイムを読み込む見込みがあまりないと思われる場合は、ローカル URL で workbox-sw を使用することができます。

まとめ

プレキャッシュを使用せずに Workbox を使用する方法を理解したところで、特定のバンドラやビルドツールに縛られることはなくなりました。これにより、Workbox のランタイム キャッシュ コードの一部を使用して、Service Worker を柔軟に作成できます。