create-react-app で作成したReactのプロジェクトに「後付けで」PWA化する(Workbox使用)

## TL;DR

 - `create-react-app` のPWAテンプレートなしプロジェクト
 - `eject` なし
 - Webpackの設定オーバーライドなし

でWorkboxを導入してPWAを有効にできる。

## はじめに

`create-react-app` でPWA対応のReactプロジェクトを作るには `--template cra-template-pwa` というオプションでPWA対応のテンプレートを読み込む方法が見つかります。

しかし、プロジェクトの立ち上げから携わることができないケースもあります。その場合PWA対応のテンプレートを使わなかった状態から「後ほどPWA対応にする方法を探す」としても見つかりづらかったです(2021/08/18現在)

また、 `create-react-app` で作成したプロジェクトは `eject` コマンドを使って設定ファイルを抽出しないとWebpackの設定を変更するのが難しくなります。一度ejectするとその前の状態に戻せないのでejectを選択しない場合もあります。 
ejectせずにWebpackの設定をオーバーライドする方法(react-app-rewiredなど)を試しましたが正しく設定することができませんでした。

そこで、PWA未対応のReactプロジェクトに導入する方法としてWorkboxをインストールする方法が正しく動作したので、導入手順を残そうと思います。

## 環境

```bash
$ node --version
v14.17.3

$ npm --version
6.14.13

$ yarn --version
1.22.11
```

## Workbox パッケージのインストール
 
 `workbox wizard` を使ってWorkboxに必要なファイルを生成します。使うのはこの1回限りになりますが、 `npx` が使えないためworkbox-cliをグローバルでインストールする必要があります。
 
```bash
npm i -g workbox-cli
```
 
次に、Reactのプロジェクトディレクトリ内でwizardを実行します。
 
```bash
workbox wizard
```
 
いくつか質問があるので答えていきます。
 
> ? What is the root of your web app (i.e. which directory do you deploy)?
 
そのうち、Workboxが生成するService Workerの出力先を尋ねられます。 ここでは`public` を選択します。

「デプロイ先」ということで `build` を選択しそうですが、これではReactが読み込んでくれないので `public` に設定します。
 
この設定にするとdevelopmentビルドで確認することができ、productionビルドの場合はあとで `yarn build` コマンドを実行すると `build` にService Workerのファイルも配置されます。
 
そのほかのオプションは特に変更しません。

```bash
$ workbox wizard
? What is the root of your web app (i.e. which directory do you deploy)? public/
? Which file types would you like to precache? ico, html, png, json, txt
? Where would you like your service worker file to be saved? public/sw.js
? Where would you like to save these configuration options? workbox-config.js
? Does your web app manifest include search parameter(s) in the 'start_url', other than 'utm_' or 'fbclid' (like '?source=pwa')? No
To build your service worker, run

  workbox generateSW workbox-config.js

as part of a build process. See https://goo.gl/fdTQBf for details.
You can further customize your service worker by making changes to workbox-config.js. See https://goo.gl/gVo87N for details.
```


## Workboxのインストール(プロジェクト, devDependencies)
 
`workbox wizard` で生成したWorkboxの設定ファイルからService Workerを生成するにはプロジェクト側にWorkboxを導入します。
 
[PWA対応のテンプレートを使ったReactプロジェクト](https://github.com/cra-template/pwa/blob/94bad4b672bd6072f8ef8ea25093451e9a1a67b7/packages/cra-template-pwa-typescript/template.json) (`--template cra-template-pwa` オプションありでプロジェクトを立ち上げた場合)を参考にしています。不要なパッケージがあるかもしれません。
 
```bash
$ yarn add -D workbox-background-sync　workbox-broadcast-update　workbox-cacheable-response　workbox-core　workbox-expiration　workbox-google-analytics　workbox-navigation-preload　workbox-precaching　workbox-range-requests　workbox-routing　workbox-strategies　workbox-streams
```
 
 
## serviceWorkerRegistration.js の移植

PWA対応のテンプレートを使ったReactプロジェクトにある [serviceWorkerRegistration.js](https://github.com/cra-template/pwa/blob/3ffd5b0aece2c58067788460801ccc664ecc4004/packages/cra-template-pwa/template/src/serviceWorkerRegistration.js)を `src` ディレクトリに配置します。

## Service Workerの設定
 
`serviceWorkerRegistration.js` で読み込むService Workerのファイル名が異なるのでここで変更します。
 
```js:src/serviceWorkerRegistration.js
    window.addEventListener('load', () => {
      const swUrl = `${process.env.PUBLIC_URL}/sw.js`;
``` 

`index.js` で  `serviceWorkerRegistration.js` を読み込んでService Workerをregisterするように設定します。

先頭にファイルを読み込むコードを追加します。

```js:src/index.js
import * as serviceWorkerRegistration from './serviceWorkerRegistration';
```
 
読み込んだ `serviceWorkerRegistration.js` を使ってService Workerを登録するように次のコードを末尾に追加します。

```js:src/index.js
serviceWorkerRegistration.register();
```

## Service Workerの生成
 
こちらはWorkboxのコマンドを用います。 **デフォルトではソースマップも出力されるため** 設定を変更するなどでproductionビルドに混入しないよう注意する必要があります(回避方法は未確認)
 
 
```bash
$ workbox generateSW workbox-config.js
```
  
## 動作確認(localhostで可能)
 
 `serviceWorkerRegistration.js` はデフォルトでdevelopmentビルドのPWAを無効にする設定があるため、developmentで確認したい場合はそれを外します。
productionコードであるかどうかの条件分岐があるのでそこを外します。

```js:src/serviceWorkerRegistration.js(変更前)
export function register(config) {
  if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {
``` 


```js:src/serviceWorkerRegistration.js(変更後)
export function register(config) {
  if ('serviceWorker' in navigator) {
``` 

そして、devサーバーを起動してブラウザで開発者ツールを開き、ApplicationのService Workersか、LighthouseでProgressive Web Appのチェックを入れて確認できます。

```bash
$ yarn start
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/216963/84068a9c-9919-474c-b8ff-5526f5f2a90f.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/216963/6f9824c3-627a-76a6-cec2-7d8d3cb70ebf.png)


PWAになりましたね！
本記事ではPWA化までを説明したので、この後は足りない部分を修正したり、実際にホスティングサービスへ公開したりしましょう。

## 参考

create-react-appで作ったReact SPAにWorkbox-CLIでService Workerをねじ込む | WP Kyoto
https://wp-kyoto.net/replace-workbox-cli-into-create-react-app-app/