Vue.jsをPWA化してService WorkerにRuntime Cacheを実装、そして公開するまで


はじめに

先日iPhoneを買ったので何か面白いことできないかなと思い、PWAを作りました。
本記事はVue.jsをPWA化し、Service WorkerにRuntime Cacheを実装してそれを公開する手順について紹介します。(本当はWeb Pushなんかもやってみたかったですが、2019/09現在、Safariは未対応のためできず。。)
Vue.jsやTypeScriptの書き方は解説しません。

今回の成果物

今回はQiitaのAPI(指定したタグが付けれられた投稿一覧を返すAPI)の結果をService Workerにキャッシュさせ、一度検索したものはオフラインでも取得できるようなPWAを作成しました。
以下はそのデモ動画になります。

動作の解説

起動後ネットワークが接続された状態で、まず「java」というタグで検索します。
するとjavaというタグの投稿一覧が表示されます。
その後iPhoneのネットワークを切断します。(ここでオフライン状態)
切断後「ruby」というタグで検索するとオフラインのため結果を取得できず表示されないことが分かります。
しかし、次に再度「java」というタグで検索するとオフラインにも関わらず投稿一覧が表示できていることが分かります。

アプリケーションとソースコード

アプリケーションはGithub Pagesに公開しています。
https://kawakawaryuryu.github.io/vue-sample-pwa/

ソースコードは以下になります。
https://github.com/kawakawaryuryu/vue-sample-pwa

利用時の注意

QiitaのAPIは認証なしで使用しています。そのため、1時間に60回以上たたくと利用制限に引っかかり使用できなくなりますので、検索のしすぎにはご注意ください。

Qiita API v2ドキュメント - Qiita:Developer

利用制限
認証している状態ではユーザごとに1時間に1000回まで、認証していない状態ではIPアドレスごとに1時間に60回までリクエストを受け付けます。

前提

使用したツールやライブラリとそのバージョンは以下になります。

ツール・ライブラリ バージョン
Vue.js 2.6.10
TypeScript 3.4.3
@vue/cli-plugin-pwa 3.10.0
axios 0.19.0

また、あらかじめVue.jsのプロジェクトが作成されており、TypeScriptがインストールされている前提で進めます。

事前知識

必要となる知識は「PWAとは何かまとめる - Qiita」にまとめてありますのでご覧ください。

Vue.jsでのPWA化手順

ここからは実際にVue.jsをPWA化する手順を紹介します。
正直あっという間です。

1. Vue CLIのPWAプラグインの追加

Vue.jsにはPWAプラグインがあらかじめ用意されています。
これを使うとPWAに必要なファイルやライブラリをインストールすることができます。
今回はこれを使ってPWA実装を行っていきます。

$ yarn add @vue/cli-plugin-pwa --dev

2. PWAプラグインを使って必要なソースや設定一式をインストール

以下コマンド実行でPWAに必要なファイルやライブラリを追加します。

$ vue add pwa

実行すると以下のファイルたちが追加or更新されます。

     public/img/icons/android-chrome-192x192.png
     public/img/icons/android-chrome-512x512.png
     public/img/icons/apple-touch-icon-120x120.png
     public/img/icons/apple-touch-icon-152x152.png
     public/img/icons/apple-touch-icon-180x180.png
     public/img/icons/apple-touch-icon-60x60.png
     public/img/icons/apple-touch-icon-76x76.png
     public/img/icons/apple-touch-icon.png
     public/img/icons/favicon-16x16.png
     public/img/icons/favicon-32x32.png
     public/img/icons/msapplication-icon-144x144.png
     public/img/icons/mstile-150x150.png
     public/img/icons/safari-pinned-tab.svg
     public/manifest.json
     public/robots.txt
     src/registerServiceWorker.ts
     package.json
     src/main.ts
     yarn.lock
ファイル 内容
public/img/icons/*.png 端末に合わせたアイコン画像
public/manifest.json Web App Manifest
PWAとして端末にインストール可能にするために必要
src/registerServiceWorker.ts Service Workerの登録を担うファイル
インストールするとmain.tsに勝手にimportしてくれるので、起動時にService Workerが自動で登録される

参考

ここまででPWAの下準備は整いました。(早い)
これだけでもPWAとして動かすことは可能ですが、ここからはService WorkerにAPIレスポンス結果をキャッシュさせるような実装を追加してPWAをより進化させていきます。

Runtime Cacheの実装

次に、Service WorkerにQiitaのAPIレスポンス結果をキャッシュするようなRuntime Cacheを実装していきます。

Workbox

Vue.jsのPWAプラグインを利用するとService Workerをゴリゴリ実装せずとも設定だけで簡単にPWAの色々な機能を実現することができます。
Runtime Cacheも設定だけで実現可能です。
それを実現してくれるのがWorkboxというライブラリです。
WorkboxはGoogleが提供するService Workerの実装ライブラリで、ボイラープレートになりがちな実装コードを提供してくれます。
そのため、開発者がJavaScriptを書かなくとも設定だけでService Workerの機能を利用することが可能です。
Vue.jsのPWAプラグインは内部でWorkboxを使用しています。
次から具体的なRuntime Cacheの実装までの手順を見ていきますが、内部にはこのWorkboxがいると思ってください。

1. APIコールを実装する

まず、QiitaのAPIを叩く部分の実装です。
HTTPクライアントとして、axiosをインストールします。

$ yarn add axios

以下の実装はaxiosを用いてQiitaのAPIを叩き、指定したタグが付けられた記事一覧を取得し表示するコンポーネントになります。

QiitaSearch.vue
<template>
  <div>
    <input type="text" v-model="tag" />
    <button @click="search(tag)">search</button>
    <ul>
      <li v-for="article of articles" v-bind:key="article.id">
        {{ article.url }}
      </li>
    </ul>
  </div>
</template>

<script lang="ts">
import { Component, Vue } from 'vue-property-decorator'
import { QiitaArticle } from '@/interface/QiitaArticle'
import axios, { AxiosResponse } from 'axios'

@Component
export default class QiitaSearch extends Vue {
  private articles: QiitaArticle[] = []
  private tag: string = ''

  public created() {
    this.articles = []
  }

  public async search(tag: string) {
    try {
      const response: AxiosResponse = await axios.get<QiitaArticle[]>(
        `https://qiita.com/api/v2/tags/${tag}/items`
      )
      this.articles = response.data
    } catch (e) {
      this.articles = []
    }
  }
}
</script>

2. vue.config.jsの設定

vue.config.jsに以下のような設定を書くだけでRuntime Cacheの実装は終了です。
設定ファイルはvue.config.jsですが、設定項目はWorkboxのものと同じになります。
詳しい設定項目についてはWorkbox webpack Plugins  |  Workbox  |  Google Developersを参照してください。

vue.config.js
module.exports = {
  ...
  pwa: {
    workboxOptions: {
      runtimeCaching: [
        {
          urlPattern: /https:\/\/qiita.com\/api\/.*/,
          handler: 'networkFirst',
          options: {
            cacheName: 'qiita-cache',
            expiration: {
              maxEntries: 10,
              maxAgeSeconds: 300
            },
            cacheableResponse: {
              statuses: [0, 200]
            }
          }
        }
      ]
    }
  },
  ...
}

GitHub Pagesに公開する

最後に今回作成したPWAサンプルを公開する手順です。
冒頭でも述べた通り今回作成したPWAサンプルをGitHub Pagesに公開しました。

続いてはその手順を紹介。

1. vue.config.jsの修正

GitHub Pagesに公開するため、ビルドした際のindex.htmlの出力先をdocsという名前のディレクトリにします(デフォルトはdist)。
また、GitHub Pagesはリポジトリ名がサブパスとして付加されるので、ルートパスを変更します。

vue.config.js
module.exports = {
  ...
  outputDir: 'docs',
  publicPath: '/vue-sample-pwa/',
  ...
}

参考

2. eslintからdocsを外す

docsディレクトリはeslintの対象外としたいので以下ファイルを追加。

.eslintignore
docs/**

参考

3. ビルド

デプロイするコードを生成します。
これによりdocsディレクトリが作成されます。

$ yarn build

4. GitHub上でGitHub Pagesとして公開するように設定する

Repository名 > Settings > Optionsを選択し、GitHub Pagesの設定箇所で以下ように「None」→「master branch /docs folder」を選択します。

参考

5. git push

実際にGitHub Pagesに公開します。

$ git push origin master

これでPWAがGitHub Pagesに公開されるようになりました!

以上