PWA はじめ方

## この内容について
この内容は、PWAをはじめたい方に向けた基礎的な記事になります。

## PWAとは (ピーダブリューエー)
**P**rogressive **W**eb **A**ppsの略で、
Webサイトをアプリのように使えるようにする仕組みのこと。
HTTPS化・レスポンシブ・PWA特有のコードの記述が必須で、
PWAを実装することで、プッシュ通知やホーム画面へのアイコン追加ができ、
アプリストアでインストールすることなくアプリのような操作感となります。
[PWA公式サイト](https://web.dev/progressive-web-apps/)

## 導入方法
### WebサイトをSSL化する
PWAはServiceWorkerが必須で、ServiceWorkerはSSL対応されたサイトでのみ
動作するので、WebサイトをSSL化しましょう。

### マニフェストファイルの作成
manifest.jsonという名前でファイルを作成します。
このファイルにPWAの基本的な設定を記述します。

```json:mainfest.json
{
  "name": "Webサイト名",
  "short_name": "Webサイト名",
  "description": "Webサイトの説明",
  "start_url": "/",
  "scope": "/",
  "theme_color": "#444444",
  "background_color": "#ffffff",
  "display": "standalone",
  "orientation": "any",
  "lang": "ja-jp",
  "icons": [{
      "src": "apple-touch-icon-192×192.png",
      "sizes": "192x192",
      "type": "image/png"
    }, {
      "src": "apple-touch-icon-512×512.png",
      "sizes": "512x512",
      "type": "image/png"
  }]
}
```

| プロパティ | 説明 |
|:--|:--|
| name | Webサイト(アプリ)の正式名称を記述します。 |
| short_name | ホーム画面に登録した際に表示される名前を記述します。 |
| description | Webサイトの説明を記述します。 |
| start_url | アプリを立ち上げた際に始めに開かれるWebページを記述します。トップページであれば「/」と記述。 |
| scope | 特定のURL以下をPWA化したい場合に記述します。start_urlと同じでOK。 |
| theme_color | Webアプリのテーマカラーを記述します。タブやメニューバーの色がこの色です。 |
| background_color | Webアプリの背景色を記述します。スプラッシュ画面の色もこの色です。 |
| display | WebアプリのUIの指定を記述します。「standalone」でアプリのようにURLバーを除去します。 |
| orientation | アプリを起動した時の画面の向きを記述します。「any」は縦・横、「portrait」は縦、「landscape」は横。 |
| lang | Webアプリで使用する言語を記述します。 |
| icons | ホーム画面に登録した際に表示されるアイコンを記述します。192×192と512×51２の画像を用意。 |

自分で記述するのが面倒な場合は、下のようなGUIで作成できるツールがあります。
[App Manifest Generator](https://app-manifest.firebaseapp.com/)

### ServiceWorkerの作成
sw.jsという名前でファイルを作成します。
このファイルにオフライン環境で読み込むスクリプトを記述します。

```js:sw.js
// 古いキャッシュ
var CACHE_OLD = ['cache-v001', 'cache-v002'];
// キャッシュ名とキャッシュファイルの指定
var CACHE_NAME = 'cache-v003';
var urlsToCache = [
  '/',
  '/styles.css',
  '/images/favicon.ico'
];
// インストール処理
self.addEventListener('install', function(event) {
  event.waitUntil(
    caches
      .open(CACHE_NAME)
      .then(function(cache) {
        return cache.addAll(urlsToCache);
      })
  );
});
// 古いキャッシュの削除
self.addEventListener('activate', function(event) {
  var cacheWhitelist = [CACHE_OLD, CACHE_NAME];
  event.waitUntil(
    caches.keys().then(function(cacheNames) {
      return Promise.all(
        cacheNames.map(function(cacheName) {
          if (cacheWhitelist.indexOf(cacheName) === -1) {
            return caches.delete(cacheName);
          }
        })
      );
    })
  );
});

// リソースフェッチ時のキャッシュロード処理
self.addEventListener('fetch', function(event) {
  event.respondWith(
    caches
      .match(event.request)
      .then(function(response) {
        return response ? response : fetch(event.request);
      })
  );
});
// 動的リクエストをクローンし、キャッシュ
function requestBackend(event){
  var url = event.request.clone();
  return fetch(url).then(function(res){
    if(!res || res.status !== 200 || res.type !== 'basic'){
      return res;
    }
    var response = res.clone();
    caches.open(CACHE_NAME).then(function(cache){
      cache.put(event.request, response);
    });
    return res;
  })
}
```

基本的に書き方はこの通りで、CACHE_OLDとCACHE_NAMEとurlsToCacheの部分を変更します。
CACHE_OLDに古いバージョンのキャッシュを、CACHE_NAMEに新しいバージョンのキャッシュを、
urlsToCacheにキャッシュしたいファイル名を記述します。

### マニフェストファイルの読み込み
PWAを適用させるWebページ全てのheadタグ内でマニフェストファイルを読み込みます。

```html:HTML
<link rel="manifest" href="./manifest.json">
```

### ServiceWorkerの読み込み
PWAを適用させるWebページ全てのbodyタグの最後でServiceWorkerを読み込みます。

```html:HTML
<script>
  if ('serviceWorker' in navigator) {
    navigator.serviceWorker.register('./sw.js').then(function(registration) {
      console.log('ServiceWorker registration successful with scope: ', registration.scope);
    }).catch(function(err) {
      console.log('ServiceWorker registration failed: ', err);
    });
  }
</script>
```

### サイトテーマカラーの設定
マニフェストファイルのtheme_colorと同じ色を指定します。

```html:HTML
<meta name="theme-color" content="#444444"/>
```

### テストする
[Lighthouse](https://chrome.google.com/webstore/detail/lighthouse/blipmdconlkpinefehnmjammfjpmpbjk)を使って、テストするのが最も簡単です。
PWAが適用されていると、Progressive Web Appに下記の画像のようにマークが表示されます。
<img width="1792" alt="スクリーンショット 2020-06-05 0.17.42.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/473780/fdc1c2bb-f38f-359f-816f-14b6bc6164df.png">


## PWAによるメリット・デメリット
### メリット

- ページの読み込みが速い
- オフラインで動く
- アプリストア申請がいらない
- インストールが必要ない
- ホームアイコンを追加が可能
- プッシュ通知が可能
- GPS機能の利用が可能

Webサイトの可能性を広げてくれて開発の自由度が上がります。

### デメリット

- アプリのホームアイコン設置を促すのが難しい
- iOSで未対応機能がある
- キャッシュ対応が難しい

特にキャッシュさせる部分をどこまでにするかを決めておかないと、
後々更新が大変で面倒です。

## まとめ
Webサイトをアプリのように使える機能を持たせることで、
ユーザ体験がより良くなるので、採用してみると面白いです。

PWAは、Googleも推奨なので、簡易的なアプリを作成したい場合に有効です。
今後は需要も増えるかと思いますので、知っておくと良いでしょう。

この記事が良いと思った方は、LGTMをしていただければ嬉しいです！
フォローも是非お願い致します(^^)