Secret Staircase

create-react-app利用時にservice workerをカスタマイズする

--2021年10月21日追記--

本記事は create-react-app v3 での方法を述べています。v4 から仕組みが変わり service-worker.js は生成されるものではなくデフォルトの実装が提供され自分で管理するようになりました。本記事で述べるようなパッチの必要はありません。

TL;DR

create-react-app がビルド時に生成するファイルをカスタマイズするために diff でパッチを作成しておいてビルド時に patch コマンドで適用しました。

create-react-app 利用時に service worker をカスタマイズする

create-react-app では service worker の実体が service-worker.js としてビルド時に生成されます。たまに(しょっちゅう?)カスタマイズしたくなるのですが生成されるものだから少し工夫が必要です。

※ 非 eject 環境です

service-worker.js はこういう内容のファイルです。今回は最後の blacklist を変更したいと思います。このままだともし /blog などでこの SPA ではない別のアプリケーションを動かしていても service worker が全部 index.html を返してしまうんですよね。

/**
 * Welcome to your Workbox-powered service worker!
 *
 * You'll need to register this file in your web app and you should
 * disable HTTP caching for this file too.
 * See https://goo.gl/nhQhGp
 *
 * The rest of the code is auto-generated. Please don't update this file
 * directly; instead, make changes to your Workbox build configuration
 * and re-run your build process.
 * See https://goo.gl/2aRDsh
 */

importScripts(
  'https://storage.googleapis.com/workbox-cdn/releases/4.3.1/workbox-sw.js'
);

importScripts('/precache-manifest.25289e9c0db09d014e37a8aef0a48861.js');

self.addEventListener('message', event => {
  if (event.data && event.data.type === 'SKIP_WAITING') {
    self.skipWaiting();
  }
});

workbox.core.clientsClaim();

/**
 * The workboxSW.precacheAndRoute() method efficiently caches and responds to
 * requests for URLs in the manifest.
 * See https://goo.gl/S9QRab
 */
self.__precacheManifest = [].concat(self.__precacheManifest || []);
workbox.precaching.precacheAndRoute(self.__precacheManifest, {});

workbox.routing.registerNavigationRoute(
  workbox.precaching.getCacheKeyForURL('/index.html'),
  {
    blacklist: [/^\/_/, /\/[^\/]+\.[^\/]+$/],
  }
);

このファイルを変更したものをビルド後に差し替えたら簡単そうですが中盤の /precatch-manifest.<hash>.js のハッシュ部分がビルドのたびに変わるのでそうはいきません。

service-worker.js をカスタマイズしたいという要求はよくあるらしくググるといくつか方法が見つかります。ここでは create-react-app のものをなるべく利用しつつ少しだけ変更するための方法を考えてみました。

簡単に言うとこんな手順です。

  • 事前にパッチを作る
  • ビルド時にパッチを適用する

事前にパッチを作る

まずビルドします。

npm run build

ファイルをコピーして変更します。

cd build
cp service-worker.js service-worker.js.new

service-worker.js.new を好きなように変更します。

パッチを作ります。

diff -u service-worker.js service-worker.js.new &gt; ../patch/service-worker.js.patch

ビルド時にパッチを適用する

package.jsonscripts に次のように patch コマンドを登録しておきます。

"scripts": {
  "start": "react-scripts start",
  ...
  "patch": "cd build && patch < ../patch/service-worker.js.patch"
},

実行

これで npm run build してから npm run patch でパッチが適用されます。

blacklist を利用する別の方法

今回のように blacklist を設定したいのであれば、デフォルトで /_ から始まる URL が設定されているのでそれを利用してもよいかもしれません。その場合は /blog ではなく /_blog としてアクセスできるようにサーバー側を変更します。

バージョン

  • create-react-app 3.0.1