avataruuki.dev

Laravel on Docker環境でWebpackのHMRが効かないケースへの対応

  • Webpack
  • Docker
  • Laravel
  • 8 min read

MPA構成のHMR指定に地味な点でこれまで何度かハマってしまったため自分用に備忘録を残しておく。

環境

  • webpack@5.74.0 (非mix)
  • webpack-dev-server@4.7.4
  • webpack-cli@4.10.0

確認すべき事

dev-serverのHMRオプションが本当に有効になっているか

config上、hot: true を指定しても有効にならないケースがある。これは、おそらく公式ドキュメントに記載されている下記の仕様が関係しており、環境によりHotModuleReplacementPluginが正常にロードされない場合に無効となるようだ。

Since webpack-dev-server v4, HMR is enabled by default. It automatically applies webpack.HotModuleReplacementPlugin which is required to enable HMR. So you don't have to add this plugin to your webpack.config.js when hot is set to true in config or via the CLI option --hot. See the HMR concepts page for more information.

現在の状態は、アプリケーション立ち上げ後にコンソール上に表示されるログで確認できる。

[webpack-dev-server] Server started: Hot Module Replacement enabled, Live Reloading disabled, Progress disabled, Overlay enabled.

もしHot Module Replacement disabledとなっている場合には、—-hot オプションを付与することで自動的にプラグインが追加される。

リバースプロキシの設定を忘れていないか

ホスト/コンテナ間でHMRを通すためWeb用に作成しているコンテナで開発サーバーのポートを内部通せるようになっている、または空けているポートと設定が間違っていないかを確認し、設定が抜けている場合、docker-composeで対象コンテナに<dev-server-port>:<web-port>を指定。(devServerとコンテナのportは重複しないようにする)

例:

web:
  build:
    ...
  ports:
    - '3081:8080'

変更を検知させようとしているファイルが正しく import されているか

Entryポイントかその範囲下でロード出来ていない場合など、Nothing hot updated. となる。

publicPath の指定が振り分けられているか

webpack.config.js、bladeそれぞれ、開発時は開発サーバーに向ける。

isDev ? 'http://localhost:3000' : '/'
http://localhost:3000/js/app.js

最終的な設定

  • Webコンテナのport設定を3081:8080とした場合
  • blade更新時のリロードを無効とする場合、明示的にliveReload: falseも指定しておく
  devServer: {
    host: 'localhost',
    port: '3000',
    client: {
      overlay: true,
      webSocketURL: 'ws://localhost:3000/ws',
    },
    proxy: {
      '*': 'http://0.0.0.0:3081/',
    },
    hot: 'only',
    liveReload: false,
    static: {
      directory: path.resolve(__dirname, 'path-to-laravel/public'),
    },
    devMiddleware: {
      writeToDisk: true,
      publicPath: 'http://localhost:3000/',
    },
    watchFiles: [
      'path-to-laravel/resources/**/*.blade.php',
      'path-to-laravel/public/css/*.css',
      'path-to-laravel/public/js/*.js',
    ],
  },

補足

  • CORSの設定は必要に応じて、allowedHostsheadersで行う
  • staticはWebpack@4のcontentBaseと同様
  • hot: 'only' は、ビルド失敗時のページ更新を無効とする設定。(ドキュメント

追記('22, 8/31)

HMRによる更新時 output.path に、変更を通知するため[name].[hash].hot-update.[ext]ファイルが出力される。
上記の構成のままではcssやjsの hot-update が出力され続けてしまうため、下記のように対応する。

Webpack 4系

※ rimraf 等を使い hot 配下を削除するような処理を任意の方法で作っておく事

module.exports = {
  ...
  output: {
    ...
    hotUpdateChunkFilename: 'hot/hot-update.js',
    hotUpdateMainFilename: 'hot/hot-update.json',
  },
}

Webpack 5系

5のリリース以降、hotUpdate*系のオプションが廃止されたというドキュメントは見つけられていないが、v5.74.0 では対応されていなかったためevent-hooks-webpack-pluginを使い対処する。
参考:Webpack 5 リリース (2020-10-10)

※ rimraf のcallback関数が未指定だとWebpackの処理が止まるため、空の関数を入れている

const rimraf = require('rimraf');
const EventHooksPlugin = require('event-hooks-webpack-plugin');

module.exports = {
  ...
  plugins: [
    ...
    new EventHooksPlugin({
      afterCompile: () => {
        rimraf('path-to-laravel/public/**/*.hot-update.json', () => {});
        rimraf('path-to-laravel/public/**/*.hot-update.js', () => {});
        rimraf('path-to-laravel/public/**/*.hot-update.js.map', () => {});
      },
    }),
  ],
}

これで、hot-update ファイルが増え続ける状態はひとまず解消される