Webpack で publicPath を実行時に設定する
TL;DR
- Webpack では実行時に
__webpack_public_path__
をセットすることで publicPath を変更することができる - CDN の有無など、環境によって publicPath が変わる場合に便利
- Rails では ASSET_HOST 環境変数を meta タグ経由などで渡すとよい
Webpack では publicPath を output.publicPath で設定するのが通常である。
ただ、 webpack build
のタイミングでは適切な publicPath を決定できないこともある。たとえば、 webpack build
の結果を焼き込んだ Docker イメージを production (CDN使用) と staging (CDNなし) に共通で使用するケースがあてはまる。
この状況対応するため、Webpack には publicPath を自動的に推定してくれる機能 がある。
しかし、 publicPath: '/'
では困る場合 (たとえば Rails では /packs/
にするのが通例)にはこの機能は使えない。
そこで、Webpack の実行時に __webpack_public_path__
を設定することで publicPath を設定する機能 を使うとよい。
これが必要になるのは Rails のようなアプリケーションサーバーと組み合わせて Webpack を使う(アセットのディレクトリを /
にしにくい)ケースが多いだろうから、以下は Rails を例にして説明する。
Rails の場合の設定例
ASSET_HOST 環境変数 (Rails.configuration.action_controller.asset_host) を meta タグに書き込み、Webpack のエントリーポイントの頭で読み込むとよい。
<head>
<meta name="asset-host" content="<%= Rails.configuration.action_controller.asset_host %>" />
<%= javascript_pack_tag 'application' %>
</head>
// import はファイル内で最初に処理される。
// __webpack_public_path__ の設定をエントリーポイント直下で行うと
// 他の import の後になってしまうので、専用のファイルに切り出し、最初の import にする
import './setPublicPath';
// import { foo } from './foo';
function getRailsAssetHost() {
const assetHostElm = document.querySelector("meta[name='asset-host']");
if (assetHostElm !== null) {
return assetHostElm.getAttribute("content");
} else {
return undefined;
}
}
const railsAssetHost = getRailsAssetHost();
if (typeof railsAssetHost !== "undefined") {
// もともと webpack.config.js で設定されていた publicPath と ASSET_HOST を結合する
const originalPublicPath = __webpack_public_path__;
__webpack_public_path__ = railsAssetHost + originalPublicPath;
}
余談
__webpack_public_path__
は Webpack のコンパイル時に __webpack_require__.p
に置き換えられるので、コンパイルされた JS 内を前者で検索しても見つからない。これは Webpack の実装詳細なので変更される可能性はある。
Discussion