Hot Reloadは、ソースコードの変更をページを完全にリロードすることなく、現在の状態を失うことなく実行中のアプリケーションに適用する技術です。単にページ全体を更新するLive Reloadとは異なり、ホットリロードは変更されたモジュールだけをその場で置き換えます。Webpack、2024によると、Hot Module Replacement(HMR)はコンポーネントの状態を保持することで、開発サイクル時間を最大80%削減します。
重要なポイント
Hot Reloadは、ソースファイルの変更をアプリケーションを再起動することなく、実行中のアプリケーションに即座に反映する開発メカニズムです。開発者はエディターでコードを編集し、ファイルを保存すると、結果がブラウザやエミュレーターに即座に表示されます。
この技術の重要な特徴はアプリケーションの状態を保持することです。メモリ内の変数、フォームのデータ、現在のナビゲーション — すべてがそのまま維持されます。開発者は目的の画面に戻るために一連の操作を繰り返す必要がありません。
Web開発では、Hot ReloadはHot Module Replacement(HMR)メカニズムを通じて実装されます。バンドラーはファイルの変更を監視し、影響を受けるモジュールを特定し、WebSocketを介して更新されたコードをブラウザに送信します。ブラウザはページをリロードせずにそれらのモジュールだけを置き換えます。
Hot Reloadの人気は、シングルページアプリケーション(SPA)の普及とともに高まりました。SPAではページを完全にリロードするとクライアントアプリケーションのすべての状態が失われます。React、Vue、AngularなどのフレームワークはHMRを主要な開発モードとして推奨しています。
HMRメカニズムは4つの段階から構成されます。サーバーサイドのバンドラーが変更されたモジュールをコンパイルし、新しいコードバージョンを含むJSONパッチを生成し、WebSocket接続を介してブラウザに送信します。ブラウザサイドのHMRランタイムがパッチを受信し、古いモジュールを新しいものに置き換え、モジュールの購読者に通知します。
Webpack Dev Serverはクライアントとの通信にWebSocketを使用します。ファイルが変更されると、バンドラーは変更のマニフェストを含むhot-update.jsとhot-update.jsonを生成します。クライアントはJSONPを介してこれらのファイルを自動的にロードします。
// webpack.config.js - HMRセットアップ
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
mode: 'development',
devServer: {
hot: true,
liveReload: false
},
plugins: [new HtmlWebpackPlugin()]
};
ブラウザのHMRランタイムは、置き換えられるモジュールがacceptをサポートしているかどうかを確認します。モジュールがmodule.hot.accept()を宣言している場合、ランタイムはその場で置き換え、更新コールバックを実行します。acceptが宣言されていない場合、HMRは親モジュールレベルにフォールバックします。
React Fast Refreshでは、このロジックはフレームワークレベルで組み込まれています — 各ファイルに手動でmodule.hot.acceptを記述する必要はありません。Babelプラグインreact-refresh/babelが、状態を失うことなく安全にコンポーネントを置き換えるコードを追加します。
これら3つの用語はしばしば混同されますが、動作は根本的に異なります。Live Reloadは変更があるたびにページ全体をリロードします — すべての状態が失われます。Full Reloadは開発サーバーが完全に再起動されたときに発生し、クライアントだけでなくサーバーの状態も失われます。
| タイプ | ページリロード | 状態保持 | 速度 |
|---|---|---|---|
| Hot Reload (HMR) | なし | あり | 即時 |
| Live Reload | あり | なし | 1~3秒 |
| Full Reload | あり | なし | 3~10秒 |
HotとLiveの選択は開発速度に直接影響します。HMRでは、“コード編集 → 結果確認”のサイクルは50~200ミリ秒です。Live Reloadでは1~3秒にコンテキスト復元時間が加わります。1日の作業では、その差は数十分の時間節約になります。
React Fast Refreshは、Reactコンポーネント向けに最適化された公式のHot Reload実装です。これは、不安定で手動設定が必要だった古いReact Hot Loaderメカニズムを置き換えました。Fast Refreshは、react-refresh/babelを介してCreate React App、Next.js、Vite、Webpackに組み込まれています。
Fast Refreshは、コードが変更されたときに関数コンポーネントの状態を正しく保持します。JSXマークアップのみを変更した場合、useStateやその他のフックはその値を保持します。フックのロジックが変更された場合、コンポーネントは再マウントされます。
// 例 - JSX変更時に状態が保持される
import React, { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>+</button>
</div>
);
}
新しいコードにコンパイルエラーが含まれている場合、Fast Refreshは状態を保持しません。コンポーネントがデフォルトでエクスポートされていない場合(named export)、状態も保持されない可能性があります。コンポーネントにはdefault exportを使用し、状態保持を保証する必要がある場合はエクスポートをmemoでラップすることを推奨します。
最新のバンドラーはHMRを提供しています。WebpackはdevServer.hotによる柔軟な設定でHMRのパイオニアです。Viteはバンドルなしで即時のHMRを実現するためにネイティブESMとesbuildを使用します。ParcelはゼロコンフィグHMRを提供します。TurbopackはVercelによるRustベースのHMRを備えた新しいバンドラーです。
| ツール | HMRタイプ | 設定 | エコシステム |
|---|---|---|---|
| Webpack 5 | WebSocket経由のHMR | devServer.hot: true | React, Vue, Angular |
| Vite | ネイティブESM HMR | 組み込み | React, Vue, Svelte |
| Parcel 2 | ゼロコンフィグHMR | 不要 | React, Vue, TS |
| Turbopack | インクリメンタルHMR | 組み込み | Next.js |
Viteは速度で際立っています — ViteのHMRはブラウザのネイティブESモジュールを通じて動作します。ファイルが変更されると、Viteはプロジェクト全体を再コンパイルせずに、import()を介してそのモジュールのみを送信します。Webpackは各モジュールを完全にコンパイルするため、大規模プロジェクトではHMRが遅くなります。
さまざまなバンドラーでのHMR設定を見てみましょう。Webpackでは、devServerにhot: trueを設定し、react-refresh/babelを追加する必要があります。Viteでは、HMRは任意のプリセットで組み込みで動作します。Next.jsでは、Fast Refreshがターボコンパイラーを通じてデフォルトで有効になっています。
// vite.config.js
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
export default defineConfig({
plugins: [react()],
server: {
hmr: {
port: 3001
}
}
});
// webpack.config.js
const ReactRefreshWebpackPlugin =
require('@pmmmwh/react-refresh-webpack-plugin');
module.exports = {
mode: 'development',
devServer: { hot: true },
plugins: [new ReactRefreshWebpackPlugin()],
module: {
rules: [
{
test: /\.jsx?$/u,
use: 'babel-loader'
}
]
}
};
HMRはすべてのシナリオで正しく動作するわけではありません。動的クラスを使用したCSSモジュールが適用されない場合があります。グローバル変数やシングルトン(Reduxストア、ルーター)はリロード時にリセットされる可能性があります。重量級ライブラリはHMR時にGCなしのモジュール置き換えによりメモリリークを発生させることがあります。
HMRの問題を解決するには、ホットコードを副作用のない純粋なコンポーネントに分離します。副作用(WebSocket購読、タイマー)はuseEffectでクリーンアップする必要があります。Reduxには、sessionStorageに状態を保存するpersisterが推奨されます。CSS Modulesやstyled-componentsなどのCSSソリューションは、追加設定なしでHMRと正しく動作します。
もう一つの制限はプロダクションでのデバッグです。HMRはプロダクションビルドでは意図的に無効化されます。ソースコードは最小化され、プロダクションでのホットリプレースメントは不可能です。プロダクションでのエラーデバッグには、HMRではなくソースマップとログ記録を使用します。
よくある質問
Hot Reloadはページをリロードせずに変更されたモジュールのみを置き換え、状態を保持します。Live Reloadはページ全体をリロードし、状態は失われ、アプリケーションは初期画面から再起動します。
Create React AppやViteを使用したプロジェクトでは、HMRはデフォルトで有効です。Webpackでは、devServer: { hot: true }と@pmmmwh/react-refresh-webpack-pluginを追加する必要があります。Next.jsではFast Refreshが設定なしで動作します。
原因は新しいコードのコンパイルエラー、defaultではなくnamed export、フックシグネチャの変更、または直接的なステート変更です。React Fast Refreshは、正しい動作に必要な場合にのみ安全にコンポーネントをリセットします。
はい、最新のバンドラーはすべてTypeScriptでHMRをサポートしています。ViteはTSトランスパイルにesbuildを使用します。Webpackはts-loaderまたは@babel/preset-typescriptとともにbabelを使用します。Fast Refreshは型指定されたコンポーネントでも正しく動作します。
ブラウザコンソールでWebSocket接続エラーを確認してください。devServer.hot: trueが有効であることを確認してください。Viteの場合は、HMRポートを確認してください。問題が解決しない場合は、ページを完全にリロード(F5)し、バンドラーコンソールを確認してください。
まとめ
ターンキー方式のモバイルアプリケーションを開発します
IT Sectrは2017年からスタートアップや企業向けにiOS・Androidアプリケーションを開発しています。私たちがご相談に乗り、最適なソリューションをご提案します。