なぜViteのHMRは「魔法」のように消えるのか?開発効率を極限まで引き上げるトラブルシューティングの極意
こんにちは。開発環境のアーキテクトとして、これまで数多のプロジェクトで「ビルドが遅い」「HMRが効かない」という地獄を見てきたエンジニアです。
皆さんが今、Viteを使っていて「ファイルを保存したのに画面が更新されない…!」というストレスを抱えているなら、まずは深呼吸してください。それは「あなたのコードが悪い」のではなく「開発サーバーとブラウザの通信が、どこかで遮断されている」だけです。
今日は、ViteのHMR(Hot Module Replacement:ホットモジュール置換)がなぜ止まるのか、その「内部で起きていること」を紐解きながら、解決のフレームワークを伝授します。
—
1. ViteのHMRの正体:なぜ「更新」が起きるのか?
ViteのHMRは、ただの「リロード」ではありません。「実行中のアプリケーションの状態を維持したまま、特定のモジュールだけを差し替える」という、非常に高度な仕組みです。
内部では、以下の手順がミリ秒単位で動いています。
1. ファイルウォッチャーがディスクの変更を検知。
2. Viteが変更されたファイルだけを再コンパイル(HMR境界の特定)。
3. WebSocket経由で、ブラウザへ「このモジュールが更新されたぞ」という信号を送る。
4. ブラウザがその信号を受け取り、古いコードを破棄して新しいコードを読み込み、メモリ上で入れ替える。
つまり、HMRが効かない原因は、「1. そもそもファイル変更が検知できていない」「2. WebSocketが繋がっていない」「3. HMRの境界を超えてアプリ全体がリセットされている」のいずれかです。
—
2. HMR地獄を脱出するチェックリスト
原因その1:ファイルシステムが変更を検知できていない(特にWSL2やDocker)
Viteはデフォルトで`chokidar`というライブラリを使用してファイル監視を行いますが、WSL2のファイルシステムとWindowsのファイルシステムを跨ぐ場合や、Dockerコンテナ内で実行する場合、OSの通知イベントが正しく伝わらないことがあります。
解決策:ポーリングモードを有効にする
`vite.config.ts`に以下の設定を追加してください。
import { defineConfig } from ‘vite’;
export default defineConfig({
server: {
watch: {
// ファイルシステムイベントに頼らず、一定間隔で変更をチェックする
usePolling: true,
// ポーリングの間隔(ミリ秒)。CPU負荷と反応速度のトレードオフ
interval: 100,
},
},
});
原因その2:ネットワーク・プロキシによるWebSocketの遮断
開発サーバーとブラウザが同じネットワークにいるはずなのに、HMRが死んでいる場合、ブラウザの開発者ツール(F12)の「Network」タブを見てください。WebSocketの接続(`ws://`)が「Pending」や「Error」になっていませんか?
解決策:明示的に接続先を指定する
特に社内プロキシやVPNを通している環境では、HMRの接続先が正しく解決できないことがあります。
export default defineConfig({
server: {
hmr: {
// ローカル開発なら ‘localhost’ を明示
host: ‘localhost’,
// 特定のポートを強制的に開く
port: 5173,
},
},
});
原因その3:HMRの境界を破壊する「副作用」
これが最も厄介です。ReactやVueにおいて、コンポーネントのトップレベルで副作用(グローバル変数の初期化や、外部ライブラリのインスタンス生成)を行っていると、HMRが「このモジュールは安全に差し替えられない」と判断し、強制的に画面をフルリロードします。
解決策:副作用を適切にカプセル化する
例えば、コンポーネントの外で`new MyLibrary()`のような初期化を行っていませんか?それらは`useEffect`や`onMounted`の中へ移動させてください。
—
3. 「HelloWorld」で原因を切り分ける
もし上記を試してもダメなら、環境自体が汚染されている可能性があります。以下の手順で「クリーンなHMR」を試してください。
1. プロジェクト直下に最小構成の `test-hmr.html` を作る
2. `main.js` で数値をカウントアップするだけの単純なコードを書く
// main.js
let count = 0;
setInterval(() => {
count++;
document.body.innerHTML = `
HMR Test: ${count}
`;
}, 1000);
これがHMRで更新されるなら、Vite本体は正常です。原因はあなたが今触っている「巨大なライブラリ」や「複雑なState管理」にあります。逆にこれも動かないなら、環境変数やNode.jsのバージョン、あるいはインストールされているプラグインの競合を疑いましょう。
—
最後に:HMRは開発の「リズム」そのもの
HMRが快適に動くことは、単に手間が減るということではありません。「コードを書く→即座に結果を見る」というフィードバックループが速くなることで、脳の集中状態(フロー)を維持できるという、エンジニアにとって最も価値のある資産を手に入れることなのです。
もしまたHMRが止まったら、「OSが、サーバーが、ブラウザが今何を言おうとしているのか」を開発者ツールで覗いてみてください。エラーメッセージの裏にある「通信の断絶」を見つけることが、最強のエンジニアへの第一歩です。
明日からのコーディングが、もっと心地よいものになりますように!