KDE BLOG

バイブス

【webpack速習】vol.5: Hot Module Replacement

webpack.js.org

上記ページの要点をまとめていきます

概要

  • Hot Module Replacement (HMR) は webpack の最も便利な機能の一つ
  • 開発時のみに利用する機能
  • アプリケーションの実行中に、完全にリロードすることなくモジュールを交換、追加、または削除することで、開発スピードが大幅にアップする

Hot Module Replacement | webpack より

HMRはどのように機能するか

1. アプリケーションにおいて

モジュールをアプリケーションに出し入れする手順

  1. アプリケーションはHMRランタイムに更新を確認するように要求
  2. ランタイムは更新を非同期にダウンロードしてアプリケーションに通知
  3. アプリケーションはランタイムに更新を適用するように要求
  4. ランタイムは更新を同期的に適用

このプロセスを自動で行うか、更新を手動で行うか設定することができる。

2. コンパイラにおいて

通常のアセットに加えて、コンパイラは以前のバージョンから新しいバージョンへの更新できるようにするために 「Update」を発行する必要がある。
Update」は2つの部分から構成される。

  1. 更新されたマニフェストJSON
  2. 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に追加する必要はないかもしれません。

DevServer | webpack より

HMRが難しい場合がある

詳細は公式ドキュメントにて Hot Module Replacement | webpack

CSSのHMR

CSSstyle-loader を使うことで、HMRが有効になる。
style-loaderCSSの依存関係が更新されたときに、背後で 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...

まとめ

  • HMRは開発時に使うことで開発スピードを上げることができる
  • HMRは webpack-dev-server を使うと簡単
    • optionshottrue にすることで有効になる
    • webpack.config.js に書く場合は webpack.HotModuleReplacementPlugin を有効にする
  • CSSのHMRでは style-loader を有効にする