morishitaです。
今回は Webpack(Webpacker)とWorkbox(Workbox webpack Plugins - GenerateSW plugin)の設定について書きます。
正確にはプリフェッチの設定というよりプリフェッチするファイルをCDNから取らせるためのURLの設定でハマりました。
多分、普通に使っていれば当たり前のことを知らなくて時間がかかってしまったという話です。
このエントリを要約すると次の二点です。
- GenerateSW plugin の
modifyUrlPrefix
はWebpack の出力には適用されない - Webpack の出力ファイルをCDNからプリキャッシュさせたいなら
publicPath
を設定する
そんなん当たり前やんと思ったら、この続きは読まなくても大丈夫です。
背景
当社のメディアサイト いこレポ | 子どもが喜ぶ遊び場・おでかけガイドは、Workboxを使ってPWA化しています。
ちなみに、次のような構成です。
- Rails 5.2
- Webpacker4 (Webpack4)
- Workbox Webpack Plugin 4.3
導入したときのことは次のエントリで紹介しました。
このときには管理画面だけで 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.
modifyUrlPrefix
も manifestTransforms
も Webpackでトランスパイルするファイルには関係ないと書いてあるではないですか!3
なーんだできないのか。
ということがわかったので別の手段を探しました。
さて、どうするか…。
思いついた方法は次の2つ。
- Webpackでトランスパイルしたファイル群もGlobする
- 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のホストに変更した上で、無事プリキャッシュに追加できました。
思わぬ副次効果
この設定変更をリリースしてしばらくして、インフラエンジニアから「いこレポのリクエスト数減ってるけど大丈夫?」との連絡を受けました。
確認してみると、アプリケーションサーバへのリクエスト数がガクッと減って、反対に平均レスポンスタイムが上がっています。
アクセスが減ってるのにレスポンスが悪化?
調べてみると、Workbox関連ファイルへのリクエストがなくっていました。
あー、これまでアプリケーションサーバにアクセスが来ていたのか。
それが、CDN に取りに行くようになったのでリクエストが減ったんですね。
しかも、それらのファイルは静的なのでレスポンスタイムが小さく、平均を押し下げていたのでしょう。なのでレスポンスタイムが大きくなってしまったんですね。
ServiceWorkerのコードも確認するとちゃんとCloudFrontのURLに変っていました。
publicPath
の設定はもっと早くやっておくべきだったんですね。
まとめ
内容的には、冒頭に書いた要約のとおりです。
書いてみるとサクッと解決した様に見えるかもですが、ここにたどり着くのに数時間かかりました。
瞬殺タスクだと思って始めたのに…。
まあ、Workbox と Webpack の理解が深まったからいいか。
最後に
アクトインディではエンジニアを募集しています。 actindi.net
-
このエントリで紹介する作業の直前に3.6->4.3へのアップグレードをしました。特に躓くポイントはありませんでした。↩
-
設定全体はworkbox を導入してServiceWorkerによるキャッシュを実装した話に示しています。↩
-
Workbox5ではトランスパイルしたファイルにも適用される様になる模様です。Release Workbox v5.0.0-alpha.0 · GoogleChrome/workbox↩