アクトインディ開発者ブログ

子供とお出かけ情報「いこーよ」を運営する、アクトインディ株式会社の開発者ブログです

Workboxのプリキャッシュの設定でハマった

morishitaです。

今回は Webpack(Webpacker)とWorkbox(Workbox webpack Plugins - GenerateSW plugin)の設定について書きます。
正確にはプリフェッチの設定というよりプリフェッチするファイルをCDNから取らせるためのURLの設定でハマりました。
多分、普通に使っていれば当たり前のことを知らなくて時間がかかってしまったという話です。

このエントリを要約すると次の二点です。

  • GenerateSW plugin の modifyUrlPrefix はWebpack の出力には適用されない
  • Webpack の出力ファイルをCDNからプリキャッシュさせたいなら publicPath を設定する

そんなん当たり前やんと思ったら、この続きは読まなくても大丈夫です。

背景

当社のメディアサイト いこレポ | 子どもが喜ぶ遊び場・おでかけガイドは、Workboxを使ってPWA化しています。

report.iko-yo.net

ちなみに、次のような構成です。

  • Rails 5.2
  • Webpacker4 (Webpack4)
  • Workbox Webpack Plugin 4.3

導入したときのことは次のエントリで紹介しました。

tech.actindi.net

このときには管理画面だけで Vue.js を使っていたのですが、最近フロント側でも使い始めたました。
ちょうど Workbox のアップデートも必要になっていたのでプリキャッシュの設定も見直しました1

modifyUrlPrefix はglobしたファイルにしか適用されない

Webpack でトランスパイルする JavaScript は管理画面でしか使っていなかったので、assets ディレクトリ以下のファイル(Railsのアセットパイプラインが出力する)だけプリキャッシュするため次の様に設定していました2

  include: [/public/],
  exclude: [/<管理画面のパス>/],
  globDirectory: 'public',
  globPatterns: [
    'assets/**/*.{js,css,jpg,png,gif,webp,svg,ttf,woff}',
  ],

プリキャッシュのマニフェストにフロント側のJavascriptを追加するのは今までつけていた制限を取っ払うだけだったので簡単でした。

- include: [/public/],
- exclude: [/<管理画面のパス>/],
+ exclude: [/<管理画面のパス>/, /\.(map|gz)$/],

ソースマップと圧縮ファイル(CDNが圧縮するので不要)もプリキャッシュから除外しています。

しかし、packsディレクトリ以下の以下のファイル(Webpackerが出力する)はCDNから取るようにしたいので、パスを変更する必要がありました。

modifyUrlPrefix に次のように設定を追加してみましたが、効きません。

  modifyUrlPrefix: {
    'assets': `//${env.AWS_S3_HOST_ALIAS}/assets`, // もとからあった
+   'packs': `//${env.AWS_S3_HOST_ALIAS}/packs`    // 追加
  },

設定が良くないのかと思っていろいろ微変更してみたり、 manifestTransforms で同等の処理を実装してみたけれどダメ。

で、ドキュメントを確認してみると、次の一文が。

These options configure behavior unrelated to the webpack compilation.

modifyUrlPrefixmanifestTransforms も Webpackでトランスパイルするファイルには関係ないと書いてあるではないですか!3

なーんだできないのか。
ということがわかったので別の手段を探しました。

さて、どうするか…。

思いついた方法は次の2つ。

  1. Webpackでトランスパイルしたファイル群もGlobする
  2. Webpack の publicPath で設定する

Webpackでトランスパイルしたファイル群もGlobする

Webpackが出力したファイルもGlobしてしまえば、modifyUrlPrefixで処理できるだろうと考えました。
assets のファイルと同様の設定をpacksのファイルの分も追加しました。

  globPatterns: [
    'assets/**/*.{js,css,jpg,png,gif,webp,svg,ttf,woff}',
+   'packs/**/*.{js,css,jpg,png,gif,webp,svg,ttf,woff}',
  ],

しかし、ローカルでは動くものの本番と同じビルド環境で実行すると何故かダメ。
うまくGlobされず、プリキャッシュマニフェストが空になってしまいます。
いろいろ試してみたのですが、解決方法が見いだせず諦めました。

Webpack の publicPath で設定する

Webpack にも出力パスと外からアクセスする際のURLでパスの差異を調整するための設定があるということを思い出しWebpack側でやることにしました。

次の様に Webpacker の production.js を設定して、環境変数AWS_S3_HOST_ALIAS が設定されていれば publicPath を設定するようにしました。

process.env.NODE_ENV = process.env.NODE_ENV || 'production';

const environment = require('./environment');
const workboxConfig = require('./workbox-config');
environment.plugins.append('WorkboxConfig', workboxConfig);

if (process.env.AWS_S3_HOST_ALIAS && process.env.AWS_S3_HOST_ALIAS !== '') {
  environment.config.output.publicPath =`//${process.env.AWS_S3_HOST_ALIAS}/packs/`;
}

module.exports = environment.toWebpackConfig();

これでリクエスト先をCDNのホストに変更した上で、無事プリキャッシュに追加できました。

思わぬ副次効果

この設定変更をリリースしてしばらくして、インフラエンジニアから「いこレポのリクエスト数減ってるけど大丈夫?」との連絡を受けました。

確認してみると、アプリケーションサーバへのリクエスト数がガクッと減って、反対に平均レスポンスタイムが上がっています。

f:id:HeRo:20190802084312p:plain
急変したリクエスト数とレスポンスタイム

アクセスが減ってるのにレスポンスが悪化?
調べてみると、Workbox関連ファイルへのリクエストがなくっていました。

f:id:HeRo:20190802084425p:plain
Workboxのファイルへのアクセス

あー、これまでアプリケーションサーバにアクセスが来ていたのか。
それが、CDN に取りに行くようになったのでリクエストが減ったんですね。 しかも、それらのファイルは静的なのでレスポンスタイムが小さく、平均を押し下げていたのでしょう。なのでレスポンスタイムが大きくなってしまったんですね。

ServiceWorkerのコードも確認するとちゃんとCloudFrontのURLに変っていました。

f:id:HeRo:20190802084556p:plain
CDNから取るように変わっている

publicPathの設定はもっと早くやっておくべきだったんですね。

まとめ

内容的には、冒頭に書いた要約のとおりです。

書いてみるとサクッと解決した様に見えるかもですが、ここにたどり着くのに数時間かかりました。
瞬殺タスクだと思って始めたのに…。

まあ、Workbox と Webpack の理解が深まったからいいか。

最後に

アクトインディではエンジニアを募集しています。 actindi.net


  1. このエントリで紹介する作業の直前に3.6->4.3へのアップグレードをしました。特に躓くポイントはありませんでした。

  2. 設定全体はworkbox を導入してServiceWorkerによるキャッシュを実装した話に示しています。

  3. Workbox5ではトランスパイルしたファイルにも適用される様になる模様です。Release Workbox v5.0.0-alpha.0 · GoogleChrome/workbox