Vueのプロジェクトでworkboxを使ってみる。workboxについて説明してみる

#はじめに
必要な処理を順に書いてます。
もうそれは終わっているのだ！っというものは読み飛ばしてください(o*｡_｡)o
[workboxでキャッシュするにあたって最初から読みたい方はこの辺りから](https://qiita.com/sachiko-kame/items/e0cb84f5ccd1e6914597#%E3%82%AD%E3%83%A3%E3%83%83%E3%82%B7%E3%83%A5%E3%81%82%E3%82%8A%E7%8A%B6%E6%85%8B%E3%82%92%E5%AE%9F%E7%8F%BE%E3%81%95%E3%81%9B%E3%82%8B%E3%81%9F%E3%82%81%E3%81%AB%E4%BD%95%E3%82%92%E3%81%99%E3%82%8C%E3%81%B0%E3%81%84%E3%81%84%E3%81%8B%E6%96%87%E7%AB%A0%E3%81%AE%E3%81%BF%E3%81%A7%E7%B0%A1%E5%8D%98%E3%81%AB%E8%AA%AC%E6%98%8E)
[workboxについてはこの辺りから](https://qiita.com/sachiko-kame/items/e0cb84f5ccd1e6914597#workbox%E3%81%AB%E3%81%A4%E3%81%84%E3%81%A6%E8%A9%B3%E3%81%97%E3%81%8F)
あと少しメモ的な感じで書いているので読みにくいとかあるかもです。

workboxはgoogleさんがブラウザキャッシュするのに色々書くの大変だから使ってよのやつです。
あと、色々教えてもらえたら嬉しいです。(✿´ ꒳ ` )

あとworkboxのリンク貼っておきます。
https://developers.google.com/web/tools/workbox/

あと、今回のSampleGithubです。
https://github.com/sachiko-kame/workboxSample

ちなみにPWAアプリ目指すならキャッシュもということでPWAのタグつけてます！

#とりあえずnpm使えるようにする
**npmの私的説明**… jsコードのライブラリなどを管理するもの
**npmどうやってインストールする?**… Node.jsインストールで勝手についてくるのでNode.jsをインストールすればOK!

では早速！(｡･ω･｡)

**・Node.jsインストールまで**
:deciduous_tree:以下リンクアクセス、少し下までいくとボタンが出てくるのでタップ
https://www.npmjs.com/get-npm
https://nodejs.org/en/
<img width="793" alt="スクリーンショット 2019-09-04 1.26.38.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/133940/2fb16874-3c50-809f-b677-b22263b7df14.png">

:deciduous_tree:上記タップしたリンク先にまたもやボタンあるのでオススメの左側をタップ(右でもいいけど私はオススメされているものを使います。最新のだと安定しないとかありそうですし…)
そのあとはそのまま他のもの同様インストールまで完了してください。
<img width="632" alt="スクリーンショット 2019-09-04 1.30.11.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/133940/4b11b9ba-cc67-d0cf-be36-11e1ec3e5a58.png">

:deciduous_tree:インストールが無事出来たか確認する

```
$ node -v
```
無事完了していると、`v10.16.3`のように出てくる

```
$ npm -v
```
無事完了していると、`6.9.0`のように出てくる

:man_with_gua_pi_mao_tone5:nodeの`v10.16.3`をインストールするとバージョン`6.9.0`の`npm`がインストールされるそうですね


#npmを使ってVueプロジェクトの作成まで
:jack_o_lantern:作成したいところまで行って以下コマンド。Vue CLIとは、Vueのプロジェクトを作成するために必要なtoolみたいなものです

```
$ npm install -g @vue/cli
```
ちなみに権限ないからファイルに書き込めない的なエラーが私は出たので`sudo npm install -g @vue/cli`で私はインストールしました。

:jack_o_lantern:Vueのプロジェクトを早速作成(ここではsampleというプロジェクトで作成)
質問出てくるので適宜回答してください。

```
$ vue create sample
```

普通なら上記で『はい！出来上がり』のはずだったのですが、
ERROR　command failed: yarn とかいうエラーが…

yarnコマンドがないらしいですね…。…　…。
いいよコマンド使えるようにすればいいんでしょ(/ω＼)

```
$ npm install -g yarn
```
ちなみに権限ないからファイルに書き込めない的なエラーが私は出たので`sudo npm install -g yarn`で私はインストールしました。

```
バージョン確認
$ yarn -v

$ yarn init
```

再度実行！

```
$ vue create sample
```

完了すると最後の方に、

```
 $ cd sample
 $ yarn serve
```
っと出てくるのでその通りに打ちます！使ったプロジェクトに行ってyarnコマンドで開発環境立ち上げる的な感じです。ここが`npm serve`ならそのようにしてください…

である程度すると開発環境立ち上がり、ここにアクセスしてと出てくるのでアクセス。
http://localhost:8080/ 
<img width="877" alt="スクリーンショット 2019-09-04 2.13.30.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/133940/6545652d-e3a4-bf9d-cc73-7b92fcc53364.png">

#workboxのキャッシュができると何ができるかの説明
続いてキャッシュの実装にうつるのですが、そもそもキャッシュができると何がいい？について説明させていただきます。

キャッシュが出来ないということはインターネット接続ない場合は表示出来ない。
キャッシュがあれば事前にキャッシュしていればインターネット接続ない場合も表示できる。
でキャッシュができるといいことは…？表示が遅かったり、表示出来ないでユーザーが離脱してしまうのを少なくするのに貢献できるという感じです。

キャッシュと言われても、なんのこっちゃかわからないので実際に見てみましょう！(｡･ω･｡)

★キャッシュがない状態(普通の状態)
オフラインはデータが取得出来ていない。
![nokyasshu.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/133940/5c69462b-f561-3b21-8d34-5ed727e9cc6a.gif)

★キャッシュあり状態
オフラインでも以前データ取得していれば、データが取得出来る。
![c.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/133940/717a4573-7374-cd50-a157-a87ed742046d.gif)


#キャッシュあり状態を実現させるために何をすればいいか文章のみで簡単に説明
:cat2:**Service Workerファイルをブラウザに登録する**
※Service Workerファイルとは別に`sample.js`だろうと`sw.js`だと空だろうと構わないです。
Service Workerファイル登録作業でService Workerファイルは特別なものになります。
今回キャッシュや、ブラウザのバックグラウンド時にこのService Workerファイルなるものが使われます。

:cat2:**キャッシュしたいファイルをキャッシュする**

#キャッシュあり状態を実現させるために行うこと
:cat2:**Service Workerファイルをブラウザに登録する**
Service Workerファイルとするファイルの作成をして画面ロードで登録するといった処理。
ifのところはService Workerに対応しているブラウザならといった感じに考えてもらえれば大丈夫かと思ってます。
この状態でロードするとlogが出ていればOKです。
<img width="568" alt="スクリーンショット 2019-09-04 3.17.24.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/133940/8a87f105-a3e1-0022-b7c7-b21e78384923.png">

:cat2:**ファイルキャッシュする**
先ほどログだけ書いたところを以下のように書き換えてください。jsファイルとルートを保存しています。
行っていることはworkboxのオブジェクトを使ってキャッシュをするようにそれようのスクリプトのインストールを行い、使える場合と使えない場合のログを出しています。
下の方では、インターネット接続が出来ない時にキャッシュを使うようにしています。
`/`のindex.htmlの情報は`cache`という名前でキャッシュ。
`jsファイルは`の情報は`js-cache`という名前でキャッシュ。
で挙動はどうなるかコード下にgif画像で乗せています。

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

if (workbox) {
    console.log(`Yay! Workbox is loaded 🎉`);
} else {
    console.log(`Boo! Workbox didn't load 😬`);
}

workbox.routing.registerRoute(
    '/',
    new workbox.strategies.NetworkFirst({
        cacheName: 'cache',
    })
);

workbox.routing.registerRoute(
    new RegExp(/.*\.js/),
    new workbox.strategies.NetworkFirst({
        cacheName: 'js-cache',
    })
);
```
![workboxSample.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/133940/649215d8-dcf8-115d-c448-c293442e70e0.gif)

いかがでしょうか？
リフレッシュされるとキャッシュが登録されます。
で一度通信OFFにしてリロードすると先ほどキャッシュしたものが表示に使われるといった感じです。
今回imageはキャッシュされていないので画像は表示されていません。
再びインターネットを接続してリロードすると元からデータを取得してくれ表示されるといった感じです。

#workboxについて詳しく
##基本の基本
先ほどキャッシュをするにあたって正規表現で記述していましたが、それ以外の方法**[正規表現, 文字列, コールバック関数]**でもできます。案外これを知って入ればプレキャッシュとか知らなくてもある程度の要件満たせるのでは…なんて思ったりして…


では早速！！

###:airplane:1つめ[正規表現を使ってのキャッシュ]
これは先ほども行ったので簡単に説明。
キャッシュするものを正規表現で指定するといったもの。
正規表現の記述方法構文には、2種類ありますが、そのうちの1種類の書き方で書いてます。

正規表現の参考には以下サイトが参考になるかと思うので貼っておきます。
[JavaScript 正規表現まとめ](https://qiita.com/iLLviA/items/b6bf680cd2408edd050f)
[正規表現パターンの記述](https://developer.mozilla.org/ja/docs/Web/JavaScript/Guide/Writing_a_Regular_Expression_Pattern)

```service-worker.js
workbox.routing.registerRoute(
    new RegExp(/.*\.js/), /*ここが正規表現*/
    new workbox.strategies.NetworkFirst({
        cacheName: 'js-cache',
    })
);
```

###:airplane:2つめ[文字列使ってのキャッシュ]
これは文字列で直接指定したいなって時に使います。
〇〇.jsや〇〇.jsもって時は正規表現の方が楽かと思いますが、『favicon.ico』だけは…って時には便利かもしれないですね！

ちなみにこの書き方でもわかるようにあくまでこれは同じドメインを使用している場合のみ有効です！
もし違うドメインから取得するのなら、『/favicon.ico』のところは『https\://aaa.com/favicon.ico』のように書いてください。

```service-worker.js
workbox.routing.registerRoute(
    '/favicon.ico',
    new workbox.strategies.NetworkFirst({
        cacheName: 'icon',
    })
);
```

ちなみにこの直書きする部分ファイルの置き場所から推測するでもいいと思いますが、私はこんな感じでインターネット接続した時に出てくる項目で探したりします。
![aaa.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/133940/875bae57-e606-45b2-efb8-28c66f8a5c51.gif)


###:airplane:3つめ[コールバック関数使ってのキャッシュ]
これは正規表現や文字列のみの判断では難しいぞって時に便利かと思っています。
後正規表現だと難しいからって時にも便利かと思っています。
他にもパラメーターで判断したい時とか。

今回の場合の記述は
http\://localhost:8080/?a=aaaの時はキャッシュしたいけど、それ以外の
http\://localhost:8080/?a=bbbのような時はキャッシュしたくないといった仕様があった時を想定して作成してみました。

```service-worker.js
const matchFunction = ({url, event}) => {
  let name = url.searchParams.get("a");
  if(name == 'aaa'){
    return true;
  }else{
    return false;
  }};


workbox.routing.registerRoute(
    matchFunction,
    new workbox.strategies.NetworkFirst({
        cacheName: 'urls',
    })
);
```

一応動画も用意しました。
![abab.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/133940/f5e4f3ce-e2b8-32f3-f76d-24909f135aaa.gif)

http\://localhost:8080/?a=bbbではどんなに更新してもキャッシュできないけど、http\://localhost:8080/?a=aaaで更新するとキャッシュできることがわかると思います。
コールバックは結構便利そうです！

##細かい指定を理解する
```service-worker.js
workbox.routing.registerRoute(
    '/favicon.ico',
    new workbox.strategies.NetworkFirst({
        cacheName: 'icon',
    })
);
```
このような記述を何回もみているとふと、
『NetworkFirstって他にもパターンがあるのどういった意味なの？』
や、
『cacheName以外にも指定するものあるの？』
なんて疑問が浮かんでくると思います。

今回はここをもう少し深く知っていこうといったものです！！(✿´ ꒳ ` )

###キャッシュ戦略の指定種類
NetworkFirstの部分はキャッシュ戦略の種類らしいです。
どのようにキャッシュを活用しようか？しめしめって感じです。
項目を見ていった方がわかりやすいと思うのであげていきましょう！！

:point_up:戦略1(**Stale While Revalidate**)キャッシュあればそれを表示

```.js
new workbox.strategies.StaleWhileRevalidate({
```
リクエストのキャッシュあればそれを表示。リクエスト成功すればキャッシュを更新する。この時表示も更新される？
:man_tone3:とにかく何か表示しておきたい時に便利かも

:point_up:戦略2(**Network First**)アクセスに成功するなら成功したものを見せる。

```.js
new workbox.strategies.NetworkFirst({
```

リクエストできればリクエストしたものを表示、その時キャッシュも更新
リクエスト失敗するときは最後にキャッシュしたものを使う
:man_tone3:一番使い勝手良さそう。だからサンプルもNetworkFirsが多かったのかな


:point_up:戦略3(**Cache First**)最初にキャッシュ

```.js
new workbox.strategies.CacheFirst()
```
リクエストのキャッシュあればそれを表示。リクエスト成功すればキャッシュを更新しておく
:man_tone3:失敗したキャッシュがあるとそれを永遠に表示ってことね(/ω＼)

:point_up:戦略4(**Network Only**)ネットワークのみ

```.js
new workbox.strategies.NetworkOnly()
```

:man_tone3:ネットワークからの応答を強制って…？キャッシュはしないってことの理解でいいですか？…

:point_up:戦略5(**Cache Only**)キャッシュのみ

```.js
new workbox.strategies.CacheOnly()
```
:man_tone3:キャッシュからの応答を強制って…？


###その他指定できる
正直今までの内容でもある程度仕様などは満たせると思っています。
あくまでこれは『もっと細かく指定したいことがあるんだ！！』って方が使用するのかなっといった感じです。
例えば…
:dancer_tone3:キャッシュはこの容量までに制限するの！
:dancer_tone3:ネットワーク接続の制限時間の設定したいの！
とかの時ですね…。

★NetworkFirstの戦略の時にあまりにも時間がかかるならキャッシュを渡したいって時の設定

```service-worker.js
new workbox.strategies.NetworkFirst({
  networkTimeoutSeconds: 3,
　cacheName:　…
```


★pluginsの設定。以下項目の内容は配列内に書いていくようのものです。
https://developers.google.com/web/tools/workbox/guides/using-plugins

```service-worker.js
workbox.routing.registerRoute(
    '/favicon.ico',
    new workbox.strategies.NetworkFirst({
        cacheName: 'icon',
        plugins: [
          /*ここにカンマ使って複数指定していく*/
        ]
    })
);
```

:blush:クロスオリジンなどで失敗したリクエストをキャッシュしてしまうのを防ぐために使用
※`CacheFirst`とかにしているかたはずっと前の失敗したデータを出す状況をこのようなものを使うことで回避！ちなみに、キャッシュ戦略で`NetworkFirst`や`StaleWhileRevalidate`使って入ればそのような状況は起こり得ない！

```service-worker.js
new workbox.cacheableResponse.Plugin({
    statuses: [0, 200]
})
```

:blush:キャッシュするアイテムの個数や保持する期間を調整したい時に使います

```service-worker.js
new workbox.expiration.Plugin({
    maxEntries: 60, //個数
    maxAgeSeconds: 30 * 24 * 60 * 60, // キャッシュしたものは30日保持。それ以上日にちがたったらキャッシュから取り除く
    purgeOnQuotaError: true //使用可能なストレージを超えたらキャッシュを削除していく。デフォルトはfalse
})
```

:blush:リクエストが失敗した時に再度リクエスト送る予約みたいなもの
参考:https://qiita.com/stoneshower/items/97e5124f8f58ce9b6823

```service-worker.js
new workbox.backgroundSync.Plugin('QueueName', {
  maxRetentionTime: 24 * 60 
});
```

:blush:キャッシュした時のメッセージを受け取る的なもの？
参考:https://developers.google.com/web/tools/workbox/reference-docs/latest/workbox.broadcastUpdate.Plugin

```service-worker.js
new workbox.broadcastUpdate.Plugin({
    …
});
```

:blush:ファイルの一部分だけを取得するための方法のリクエストに対応できるように。動画の一部分PDF一部分など
参考:https://tech.jstream.jp/blog/cache/http_range_request/

```service-worker.js
new workbox.rangeRequests.Plugin({
});
```


さらにプラグインをカスタマイズさせたい方は、
https://developers.google.com/web/tools/workbox/guides/using-plugins
の『カスタムプラグイン』とか使うのもありかもしれません。


###その他指定よりもさらに細かくの時には
自分で色々書きたいときもあると思います。そんな時には、このような記述もあるそうです！

```service-worker.js
const handler = async ({url, event, params}) => {
  return new Response(
    `A ${params.type} to ${params.name}`
  );
};

workbox.routing.registerRoute(
    '/favicon.ico',
    handler
);
```

##プレキャッシュの方法(一番簡単なやつ)
参考:https://developers.google.com/web/tools/workbox/guides/precache-files/cli
参考:https://wp-kyoto.net/workbox-cli%E3%81%A7precache%E3%81%AE%E8%A8%AD%E5%AE%9A%E3%82%92%E3%81%99%E3%82%8B
※私はそこまでこれ使う必要ないのではないかと感じてしまっているのでここでは方法を簡単に紹介します。

・必要なツールのインストール

```
$ yarn global add workbox-cli
```
・ツールインストールできているかの確認のために

```
$ workbox --help
```
・構成ファイル設定コマンド。質問されるので答えていく

```
$ workbox wizard
```

指定するファイルsw.js作って置いて中に`workbox.precaching.precacheAndRoute([])`を記述しておく

・コード自動埋め込み
workbox-config.jsのところはこれ指定だったか自分で決めるか忘れてしまいました。

```
$ workbox generateSW workbox-config.js
```


##そのほかに知っておいたほうがいいと思ったもの

・開発用本番用のフラグ切り替え方法

```service-worker.js
workbox.setConfig({ debug: false });
```

・キャッシュを削除したりサービスウォーカー使えないようにするために困った時に
https://developers.google.com/web/tools/workbox/guides/troubleshoot-and-debug

#キャッシュするにあたって私が気をつけたいと思うこと！！
※あくまで私個人の感想です。
##:nerd:ログインしている状態などでみれるものはキャッシュしない方がいいと思った
キャッシュするということはデータをブラウザ上に保持しておくということ。
『どうやって？』っと言われると説明出来ないですが、もしかしたらXSSなどの脆弱性があるとキャッシュを抜き取られ個人情報が漏れるなどの危険性があると感じました。

他にもしっかり制御されていればそんなことはないかもしれないですが、パス入力して表示！ってやった時ログインしていなくても前にログインしていたデータが見れてしまうと思いました。

そのほかプロキシサーバーにキャッシュされると別の方の情報がみれてしまうなどもありました。
これに関しては`session_cache_limiter`を`public`に指定していることでプロキシサーバーにキャッシュされ同じプロキシサーバーを使っている人が誰かのキャッシュをみれてしまった的な感じです。詳しくは[キャッシュ制御不備の脆弱性にご用心](https://blog.tokumaru.org/2015/03/blog-post_27.html)や[プロキシキャッシュ対策](https://www.ipa.go.jp/security/awareness/vendor/programmingv2/contents/405.html)をご覧いただければと思います。

なのであくまで枠組みと処理の場所だけの方がいいのではないかと感じました。apiで取得するログインしている状態じゃないと見れないようなデータはキャッシュしないほうが好ましいと思いました。

##:nerd:キャッシュするときパラメーターのことも考えたいと思いました
http\://aaaa.com
http\://aaaa.com?a=aaa
のような場合『http\://aaaa.com』ならキャッシュするってなっている場合そこに付いてくるパラメーターなどもキャッシュしていいものか？っと思いました。
例えば、ワンタイムtokenなどコールバックで受け取る時に、
http\://aaaa.com?token=lfksdari090954
みたいな感じででもキャッシュ出来てしまうとパラメーターもキャッシュとして残るため危険な気がしました。

##:nerd:プレキャッシュは使わない方がいいような気がしました。
プレキャッシュの利点はもう一つのキャッシュよりもっと早くにキャッシュしているものを提供するみたいなものみたいですが、とにかくうまく処理を書かないと、サーバーのデータを変えているに、一回キャッシュ削除しないと反映されないといった挙動で、私的にあまり利点を感じませんでした。

もう一方のキャッシュでも十分に仕様などみたせると感じたため私はプレキャッシュをおすすめしません。
『どこでキャッシュしているのわからなくて…』などの記事でもプレキャッシュが使われていたりもしたので…。

もう今後一切変更しないような、データも変わらないようなサイトならいいかもしれません…。

##:nerd:なんでもかんでもキャッシュしない。容量を考える必要があると思いました。
キャッシュはいわばデータなので容量なるものがあります。
あればあるだけ使ってやろうとなると、ユーザーさんから

**『このサイトでこんなにキャッシュしているんだけど最悪っ』と言われてしまうかもしれません。**

あくまでここだけなどの線引きは必要かなと思いました。

っといっても、画像はなくてもそこまで困らなければ画像はキャッシュしなくてもいいだろうなどです。
あくまで思いやりの話をしている感じですかね？
あと、このキャッシュがなければこのキャッシュは絶対に使わないのこのキャッシュをキャッシュしているとかもダメですね(/ω＼)

言葉がキャッシュだらけでしたが、あれです。apiで取得するjsonキャッシュしているのに表示するために必要なhtmlをキャッシュしていないってことです。

**『そのデーターどこで使うんだよ！！jsonで表示する気か！！っ』**

て思われたら嫌ですもんね(/ω＼)


#参考
https://qiita.com/suisui654/items/1b89446e03991c7c2c3d
https://developers.google.com/web/tools/workbox/
https://blog.tokumaru.org/2015/03/blog-post_27.html
https://www.ipa.go.jp/security/awareness/vendor/programmingv2/contents/405.html

#最後に
またもっと理解が深まったり、追加したいことなどあれば追記していくと思います。
よろしくお願いします。

あと『これは知っておくといい』などあれば教えていただけると嬉しいです！！