【webpack速習】vol.5: Hot Module Replacement
上記ページの要点をまとめていきます
概要
- Hot Module Replacement (HMR) は webpack の最も便利な機能の一つ
- 開発時のみに利用する機能
- アプリケーションの実行中に、完全にリロードすることなくモジュールを交換、追加、または削除することで、開発スピードが大幅にアップする
Hot Module Replacement | webpack より
HMRはどのように機能するか
1. アプリケーションにおいて
モジュールをアプリケーションに出し入れする手順
- アプリケーションはHMRランタイムに更新を確認するように要求
- ランタイムは更新を非同期にダウンロードしてアプリケーションに通知
- アプリケーションはランタイムに更新を適用するように要求
- ランタイムは更新を同期的に適用
このプロセスを自動で行うか、更新を手動で行うか設定することができる。
2. コンパイラにおいて
通常のアセットに加えて、コンパイラは以前のバージョンから新しいバージョンへの更新できるようにするために 「Update
」を発行する必要がある。
「Update
」は2つの部分から構成される。
- 更新されたマニフェスト(JSON)
- 1つ以上の更新されたチャンク:データの塊(JavaScript)
マニフェストには、新しいコンパイルハッシュとすべての更新されたチャンクのリストが含まれている。
これらの各チャンクには、更新されたすべてのモジュールの新しいコード(またはそのモジュールが削除されたことを示すフラグ)が含まれている。
コンパイラは、モジュールIDとチャンクIDがこれらのビルド間で一貫していることを確認し、通常、これらのIDをメモリに保存する(例: webpack-dev-server
)。
ただし、それらをJSONファイルに保存することもできる。
3. モジュール内において
HMRは、HMRコードを含むモジュールにのみ影響するオプトイン機能(状況によって内容を変える機能)。1つの例は、style-loader を介してスタイルを修正することである。
パッチを適用するために、style-loader は HMRインタフェースを実装しており、 HMRを通じて更新を受信すると、古いスタイルを新しいものに置き換える。
同様に、HMRインターフェースをモジュールに実装するときには、モジュールが更新されたときに何が起こるべきかを describe できます。ただし、ほとんどの場合、すべてのモジュールにHMRコードを記述することは必須ではありません。
モジュールがHMRハンドラを持っていない場合、アップデートはバブルアップする。
つまり、1つのハンドラで完全なモジュールツリーを更新できる。ツリーから単一のモジュールが更新されると、依存関係のセット全体が再ロードされる。
module.hot
インターフェイスの詳細については、 HMR API page を参照。
https://webpack.js.org/api/hot-module-replacement/
4. ランタイム(実行)時において
(より込み入った技術的な話のため割愛)
はじめかた
HMRは、LiveReload の代替品として開発に使用できる。
webpack-dev-server
は、ページ全体をリロードする前に HMR で更新しようとする Hot Mode
をサポートしている。
他の機能と同様に HMRの設定をカスタム可能だが、ほとんどのケースで webpack-dev-server
が適している。
HMRの使い方
HMRを有効にする
- webpack-dev-server の設定を更新
- webpackに組み込まれているHMRプラグインを使用
▼ Webpack.config.js
const path = require('path'); const HtmlWebpackPlugin = require('html-webpack-plugin'); const CleanWebpackPlugin = require('clean-webpack-plugin'); const ExtractTextPlugin = require('extract-text-webpack-plugin'); + const webpack = require('webpack'); module.exports = { entry: { app: './src/index.js', }, output: { filename: '[name].js', path: path.resolve(__dirname, 'dist'), }, devtool: 'eval-source-map', devServer: { contentBase: './dist', + hot: true, }, module: { rules: [ { test: /\.css$/, + use: ['style-loader', 'css-loader'] - use: ExtractTextPlugin.extract({ - fallback: 'style-loader', - use: 'css-loader', - }), }, ] }, plugins: [ new CleanWebpackPlugin(['dist']), new ExtractTextPlugin('[name].css'), new HtmlWebpackPlugin({ title: 'Webpack入門 vol.3', template: './src/index.html', description: 'Webpack入門 vol.3 についてのページです' }), + new webpack.HotModuleReplacementPlugin(), ], };
これで、yarn webpack-dev-server
を実行して http://localhost:8080/
にアクセス。
CSS、print.js を修正すると、ブラウザリロードせず更新される。
index.js を更新すると、ブラウザリロードされる。
webpack.config.js で設定しない場合
コマンドからHMRを有効にすることもできる。
yarn webpack-dev-server --hot
これは webpack-dev-server の options の hot を true
にする意味。
この場合、webpack.HotModuleReplacementPlugin
は自動的に追加される模様。
webpack.HotModuleReplacementPluginは、HMRを完全に有効にするために必要です。 webpackまたはwebpack-dev-serverが--hotオプションを付けて起動された場合、このプラグインは自動的に追加されるので、これをwebpack.config.jsに追加する必要はないかもしれません。
HMRが難しい場合がある
詳細は公式ドキュメントにて Hot Module Replacement | webpack
CSSのHMR
CSSは style-loader
を使うことで、HMRが有効になる。
style-loader
はCSSの依存関係が更新されたときに、背後で module.hot.accept
を使用して <style>
タグにパッチを適用する。
▼ 有効な設定
module: { rules: [ { test: /\.css$/, use: ['style-loader', 'css-loader'], }, ] },
そのため、ExtractTextPlugin
を使って下記のような設定の場合有効にならない。
// 略 module: { rules: [ { test: /\.css$/, use: ExtractTextPlugin.extract({ fallback: 'style-loader', use: 'css-loader', }), }, ] },
開発時と本番ビルド時で分けるのが良さそう。
その他コードとフレームワーク
HMRが様々なフレームワークやライブラリと使ってもスムーズに動作するようにするためのローダーがたくさんある。
- React Hot Loader
- Vue Loader
- Elm Hot Loader
- Angular HMR
- etc...