webpackのruntimeChankでハマった
現象
react + webpackでwebアプリを作っている。
世間がreact19で騒いでいる中、今更ながらreact18にあげようと思った。
開発上ホットリロードは必須だが、react-hot-loaderはreact18に対応していないことがわかった。
代わりにreact-refresh-webpack-pluginをいれてみた。
npm install後、webpack.config.jsを編集して色々と設定していくが、ホットリロードがうまくいかない。
CMD + Sをすると、コンソール上の開発サーバーのログではビルドはされているように見えるが、画面上の更新がされない。
結論
以下を入れたら動いた。
module.exports = {
...
optimization: {
runtimeChunk: 'single',
},
};
解説
チャンクについて
以下のようなファイルがある
import app from './app.js';
export default 'the app';
このようなファイルはwebpackによるバンドルを経て、チャンク(塊)と呼ばれるものに変換される。
webpackで以下のような設定を行うことで、1つのchunkを生成することができる。
module.exports = {
entry: './index.js' // ./index.jsをバンドルしたものをデフォルト名である「main」というchunkに出力する
};
チャンクは複数に分割することもでき、これをチャンク分割と言う。
webpackでは以下のような設定を行うことで、チャンク分割ができる。
module.exports = {
entry: {
home: './home.js',
about: './about.js',
},
};
optimization.runtimeChunkとは何をやっているのか
チャンクは依存解決とかがされたただのjsのコードなので、これをeval
とかしてくれたりimport済みかどうかを管理してくれたりするwebpackの内部的なchunk(runtimeChunkという)が追加で作られる。
これをチャンク1つごとに追加のチャンクとして作るのがoptimization.runtimeChunk: 'multiple' or 'true'
。
逆にoptimization.runtimeChunk: 'single'
とすると、全てのチャンクで使われる共通のチャンクが1個だけ作られる。
runtimeChunkはそれぞれがモジュールの読み込み済み状態を管理しているらしく、これにより、モジュールが複数回読み込まれたりしてしまうことがある。
そもそもjsのモジュールは読み込みに関して冪等であるべきだが、設計上仕方なくそうなっている場合もあるらしい。
ちなみにreact-refresh-webpack-pluginのリポジトリにも以下のようなことが書いてあった。
Only one copy of both the HMR runtime and the plugin's runtime should be embedded for one Webpack app
1つのWebpackアプリには、HMRのランタイムとプラグインのランタイムの両方のコピーを1つだけ埋め込む必要があります。
これらのことから、おそらくは今回のケースはruntimeChunkが複数作られ、何回も読み込みがされたことでホットリロードが動かなかったのだと思っている。
終わりに
viteにしたいね、ほんまに。
参考
Discussion