【React】create-react-appをPWA化するのに必要な最低限の知識

現場でWebアプリ(jQuery)とモバイルアプリ(React Native)を並行して開発している身として、Webアプリをモバイル化できるというPWAに以前からとても憧れていました。

最近Reactまわりを学習し直していることもあり、この機会にcreate-react-appでPWAを作成するケースについて学んでみることにしました。

#PWA(Progressive Web Apps)とは
PWA(Progressive Web Apps)とは、ネイティブアプリと同じ勝手で使うことができるWebアプリのことをいいます。

有名どころだと、Uber, Twitter, InstagramのWebアプリで使用されており、以下のサイトでPWAのその他の例もいろいろ確認することができます。

https://appsco.pe/

Webアプリなのに以下のことができるということがわかれば、PWAがいかにすごい技術だということがイメージできるかと思います。

- ホーム画面にアイコンとして追加できる
- オフラインで開くことができる
- スマホのカメラやマップとの連携、プッシュ通知機能を使える

ブラウザによって使える機能は変わってくるのですが、以下のサイトで自分のブラウザがどの機能に対応しているかを確認することができます。

https://whatwebcando.today/

例えば、Chromeについては以下のようになっています。
大体の機能に対応していますが、SMS/MMSなどの一部の機能には対応していません。
![スクリーンショット 2021-05-13 20.52.58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569054/62605223-cf54-17dc-d01f-e664b6ddede3.png)

#PWAを構成する三要素
WebアプリをPWAと呼ぶためには以下の三要素が必要となります。
それぞれの要素を順番に説明します。

- HTTPS
- Manifest
- Service Worker

##HTTPS
位置情報やService WorkerなどのPWAに関連する機能のほとんどは、アプリがHTTPSを使用して読み込まれた場合にのみ利用できます。
HTTPSについて詳しくない方は、クライアント(Webアプリ)とサーバ間で暗号化されたデータを安全にやりとりする仕組みだと覚えておけば大丈夫です。
さらに詳しく知りたい場合は、以前私が書いた記事を参考にしていただければ幸いです。

https://qiita.com/suzuki0430/items/c6fd8dafab04fa507150

##Manifest
Manifestは、アプリがユーザにどのように表示されるかを制御するJSONファイルです。
アプリの名前、概要、アイコン、起動時の画面の向きなど、アプリの動作以外に関する詳細が記述されています。

```json:manifest.json
{
  "short_name": "React App",
  "name": "Create React App Sample",
  "icons": [
    {
      "src": "favicon.ico",
      "sizes": "64x64 32x32 24x24 16x16",
      "type": "image/x-icon"
    }
  ],
  "start_url": "./index.html",
  "display": "standalone",
  "theme_color": "#000000",
  "background_color": "#ffffff"
}
```

以下は、Twitterにおけるdev-toolのApplicationタブの表示です。
Manifestに記述した内容が反映されていることを確認することができます。
![スクリーンショット 2021-05-13 7.21.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569054/ce1ff82e-2a05-b249-3c8b-e97dec39f36f.png)

##Service Workers
Service Workerは、Webアプリとサーバのネットワークを仲介するプロキシのような役割をもっており、Webアプリのオフライン対応やバックグラウンドでの同期処理、プッシュ通知などを提供する基盤となります。
Webアプリ(ブラウザ)がサーバからデータを取得する際に、初めて取得するものであればService Workerがそのデータをキャッシュに保存します。
二度目以降の通信ではキャッシュのデータを利用するので、オフライン(サーバとつながっていない)でもWebアプリを使い続けることができます。

Service WorkerのようなプロキシはWebアプリとサーバ間の通信に割り込むため、この機能が悪用されてサーバに攻撃が加えられてしまう恐れがあります。
そのようなリスクを避けるため、前述のHTTPSによる暗号化通信が必須となります。
![スクリーンショット 2021-05-13 21.58.04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569054/898d745a-33aa-9a57-9096-66275e9ff59e.png)

また、Manifestと同様に、Service Workerの設定もdev-toolのApplicationタブより確認することができます。
![スクリーンショット 2021-05-13 7.46.45.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569054/6a3ee470-9bf8-e0ae-b4a7-81eacc798449.png)

Cache Storageよりキャッシュの中身も確認することができます。
![スクリーンショット 2021-05-13 7.55.04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569054/66a8de1d-1e18-d1e3-b499-c844b72e49ff.png)

#create-react-appプロジェクトでPWAを新規作成
create-react-appにはworkbox-webpack-pluginというものが含まれているので、他のツールをインストールすることなく簡単にService Workerをつくることができます。
また、最初からPWAアプリをつくる目的であれば、service-worker.jsとその他のモックを作ってくれるテンプレートを使用することができます。

JavaScriptの場合

```
npx create-react-app [プロジェクト名] --template cra-template-pwa
```
TypeScriptの場合

```
npx create-react-app [プロジェクト名] --template cra-template-pwa-typescript
```

プロジェクトを作成すると以下のようなフォルダ構成となります。
`manifest.json`や`service-worker.ts(.js)`が作成されています。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569054/13c01b80-740a-2c1c-1bde-3e11b86706d0.png" width="300">

`index.tsx`の中身をみると以下のようになっています。
`serviceWorkerRegistration.unregister();`を`serviceWorkerRegistration.register();`に置き換えるだけでPWA化完了です。

```tsx:index.tsx 
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import * as serviceWorkerRegistration from './serviceWorkerRegistration';
import reportWebVitals from './reportWebVitals';

ReactDOM.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>,
  document.getElementById('root')
);

// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: https://cra.link/PWA
serviceWorkerRegistration.unregister();

// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();
```
#既存create-react-appプロジェクトのPWA化
既存のプロジェクトをPWA化したい場合は以下のステップを踏めば大丈夫です。

1. 必要なファイルをコピーするために`npx create-react-app [プロジェクト名] --template cra-template-pwa`で適当なプロジェクトを作成
2. `service-worker.js`とserviceWorkerRegistration.js`をコピーして既存プロジェクトの`src`直下に配置
3. `package.json`の`workbox-`で始まるdependenciesをコピーして既存プロジェクトの`package.json`にペースト
4. `npm`か`yarn`でパッケージを再インストール

#おわりに
どうやら5月はフロントエンド強化月間らしい...

https://qiita.com/official-events/b9ad63394fa2635dfca9

#参考資料

https://developer.mozilla.org/ja/docs/Web/Progressive_web_apps

https://laboradian.com/create-installable-pwa-website/

https://developer.mozilla.org/ja/docs/Web/Progressive_web_apps/Installable_PWAs#requirements

https://developer.mozilla.org/ja/docs/Web/Progressive_web_apps/Offline_Service_workers

http://debuggerdotbreak.judahgabriel.com/2018/04/13/i-built-a-pwa-and-published-it-in-3-app-stores-heres-what-i-learned/

https://medium.com/@firt/progressive-web-apps-on-ios-are-here-d00430dee3a7