igataをPWA化する3


はじめに

最初の記事でigataにserviceWorkerを入れ込んだと思い込んでいたら正常に動いてないことがわかりました(普通は確かめるものですが、@ufoo68は重度の面倒臭がりなので記事を書いた後で調べてわかりました)。どうやって確かめたかというと(やり方は色々ありますが)私は、

の2つをやってみました。それぞれの結果をみて、明らかにServiceWorkerがちゃんと登録されていないことがわかりました。今回はそれを改善したのでそれについてまとめます(つまりは進捗プラマイゼロ)

やったこと

当初はReact側の親元のファイルであるmain.tsxにServiceWorkerを呼び出すようなスクリプトを書いていましたが、それがもしかしたら相性が悪いのかなと思い(実際コンソールでよくわからないエラーを吐いていた)、webpackの方でどうにかならないかなと調べてみました。すると2つのいい感じの記事が見つかりました。

webpackでServiceWorkerを楽に登録する

調べてみる感じ、workbox-swworkbox-webpack-pluginを使えばwebpackでServiceWorker関係のコードを生成してくれるみたいな気がします。早速yarn addしました。

yarn add -D workbox-sw workbox-webpack-plugin

あとは元のigataのwebpack.common.jspluginsを拡張します。

webpack.common.js
const plugins = [
  new HtmlWebpackPlugin({
    favicon: param.faviconPath,
    templateParameters: { title: param.title },
    template: param.templatePath,
  }),
  new MiniCssExtractPlugin({
    filename: '[name].css',
    chunkFilename: '[id].css',
  }),
  new WorkboxWebpackPlugin.GenerateSW({
    swDest: 'sw.js',
    clientsClaim: true,
    skipWaiting: true,
  }),
]

このWorkboxWebpackPlugin.GenerateSWsw.jsがServiceWorkerのスクリプトを生成してくれます。今回は最小限の設定のみ行いました。
あとはこのsw.jsを登録してもらうようにsrc/assets/html/template.htmlを拡張します。

src/assets/html/template.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>

スクリプトの内容は先ほどの記事から拝借しました。React的にはHTMLにJavaScript書くのはよろしくないのかもしれませんが、これは単純にsw.jsを呼び出すだけの単純なものなので良しとしましょう(そもそも別スレッドで動くものだし。。という言い訳もしてみる)。

あとはついでにmanifest.webmanifestを登録してないともPWA bulderに怒られたのでこれも追加することにしました。やったこととしてはsrc/assets/manifestというフォルダを新しく作って、そこにマニフェストファイルを作成しました。

src/assets/manifest/manifest.webmanifest
{
    "dir" : "ltr",
    "lang" : "en",
    "name" : "igata",
    "scope" : "/",
    "display" : "browser",
    "start_url" : "https://igata-pwa.firebaseapp.com/",
    "short_name" : "igata",
    "theme_color" : "transparent",
    "description" : "",
    "orientation" : "any",
    "background_color" : "transparent",
    "related_applications" : [],
    "prefer_related_applications" : false,
    "icons" : [
        {
            "src": "https://igata-pwa.firebaseapp.com/favicon.ico",
            "sizes": "16x16 24x24 32x32 64x64"
        }],
    "url" : "https://igata-pwa.firebaseapp.com",
    "generated" : "true"
}

このファイルの内容はPWA bulderが勝手に自動生成してくれました。あとは先ほどのtemplate.htmlに以下を追加します。

src/assets/html/template.html
<link rel="manifest" href="/manifest.webmanifest" />

このmanifest.webmanifestをwebpackの出力先にちゃんと出力してくれるようにします。今回はcopy-webpack-pluginを使いました。

yarn add -D copy-webpack-plugin

ここを読んで見つけました。とりあえずまたigataのwebpack.common.jspluginsを拡張します。

webpack.common.js
const plugins = [
  new HtmlWebpackPlugin({
    favicon: param.faviconPath,
    templateParameters: { title: param.title },
    template: param.templatePath,
  }),
  new MiniCssExtractPlugin({
    filename: '[name].css',
    chunkFilename: '[id].css',
  }),
  new WorkboxWebpackPlugin.GenerateSW({
    swDest: 'sw.js',
    clientsClaim: true,
    skipWaiting: true,
  }),
  new CopyWebpackPlugin(['src/assets/manifest/manifest.webmanifest']),
]

あとは、これでmanifest.webmanifestもちゃんと出力してくれます。

結果

とりあえず一部をのぞいてPWAの対応ができたっぽいです。

残るはプッシュ通知ですね。

さいごに

プッシュ通知はまたどこかで対応するとして、次からは本格的にアプリを作っていきたいです。