webpack-dev-server で変更が反映されない(HMR が効かない)
webpack-dev-server v4 以降は HMR が既定で有効なので、効かないときは設定を足すより先に切り分ける。
ファイル変更が検知されているか、置換を受け取るコード側が対応しているか、フルリロードに落ちていないかの 3 点を順に確認する。
公開:
要約
「保存しても画面が変わらない」とき、まず devServer.hot: true を足そうとしがちだが、DevServer の公式ドキュメント(新しいタブで開く) のとおり webpack-dev-server v4 以降では hot は既定で有効である。HotModuleReplacementPlugin も自動で適用されるため、設定を足しても状況は変わらない。
切り分けるべきは 3 段階である。
ファイル変更が検知されているか、ビルドが成功しているか、そのうえで置換がブラウザ側に届いているか。
どこで止まっているかで対処が変わる。
<i> [webpack-dev-server] Project is running at:
<i> [webpack-dev-server] Loopback: http://localhost:8080/
webpack compiled successfully保存しても compiled successfully が再度出ないなら、そもそも変更が検知されていない。
よくある原因
- 変更が検知されていない: Docker のバインドマウント、WSL 越しの Windows ファイルシステム、ネットワークドライブでは、ファイル変更のイベントがコンテナや Linux 側へ届かないことがある。
保存しても再ビルドのログが出ない状態になる。 - 監視対象外のファイル: バンドルに含まれない HTML テンプレートや
public/の静的ファイルは、依存グラフに入っていないため編集しても再ビルドされない。 - 置換を受け取れない: HMR は、更新されたモジュールを受け入れる実装があって初めて差分置換になる。
React Fast Refresh のような仕組みが入っていない素の構成では、受け入れ先が無いためフルリロードに落ちる。 - WebSocket が繋がらない: リバースプロキシや別ポート越しに開いていると、HMR の通信先が実際の配信元とずれて接続できない。
ブラウザのコンソールに接続失敗が出る。 - ビルドが失敗している: 型エラーや構文エラーで新しいバンドルが作られていない。
ターミナルにエラーが出ていれば、画面が変わらないのは当然である。
解決策
1. どこで止まっているか見る
保存したときに次の順で確認する。
ターミナルに再ビルドのログが出るか、出たあとブラウザのコンソールに [HMR] の行が出るか。
前者で止まっていれば監視の問題、後者で止まっていれば配信・受け入れの問題である。
2. コンテナや仮想ドライブではポーリングに切り替える
// webpack.config.js
module.exports = {
devServer: {
watchFiles: {
paths: ["src/**/*"],
options: { usePolling: true, interval: 300 },
},
},
};ポーリングは CPU を使うので、監視対象を必要な範囲に絞る。
3. バンドル外のファイルを監視対象に加える
devServer: {
watchFiles: ["src/**/*.html", "public/**/*"],
},依存グラフに入らないファイルは、ここに書いて初めて再読み込みの対象になる。
4. フルリロードへの取りこぼしを可視化する
devServer: {
hot: "only",
},'only' にすると、HMR が失敗したときにフルリロードへ落ちずにそのまま止まる。
「実は毎回リロードしていただけ」という状態に気づける。
5. プロキシ配下では接続先を明示する
devServer: {
client: {
webSocketURL: "ws://localhost:8080/ws",
},
},配信元と見えているホスト・ポートが違う構成では、これを指定しないと HMR の通信だけが繋がらない。