expressとwebpackでHMRする最小構成を試す
expressとは
Node.jsのフレームワークです。今回は、開発環境向けにWEBサーバを簡単に立ち上げるために使用します。
公式サイト
webpackとは
別の記事(webpackの最小構成を試す)が参考になります。
公式サイト
expressとwebpackを組み合わせる利点
WEBサーバー(express)からwebpackを使用することで、コマンドラインからwebpackを起動させる必要がなくなります。
CSS/JSファイル等を編集する度に、自動でバンドルファイルが生成されるので便利です。
ですが、編集結果をブラウザに反映させるためにはリロード操作(F5を押す等)が必要です。
そこで、今回は更に開発を便利にすることができるHMR(Hot Module Replacement)を使ってみます。
※webpack自体がWEBサーバーを立ち上げる機能も持っていますが、ここでは触れません。
HMR(Hot Module Replacement)とは?
CSS/JSのソースコードを編集した時に、リロード操作(F5を押す等)無しで編集結果がブラウザに反映される仕組みを提供してくれるwebpackの機能です。
WEBサーバー(express)からwebpackを常時使用することで、webpackはソースコードが編集されたかどうかを監視し、自動でバンドルファイルを生成します。
HMRを使うと、そこから更に編集された内容をブラウザ側に通知し、ブラウザ側は編集結果を即座に反映させることができます。
ただ、指定の箇所のみ編集が反映されるので、HMRに対応する設計をする必要はあります。
※編集する度にアプリケーション全体を自動でリロードする機能をwebpack-dev-serverが提供していますが、今回は触れません。
手を動かして試す
今回作った物のリポジトリです。
try-express-webpack-hmr-demo
以下がインストールされていることを前提とします。
- node.js
- npm
以下が手順の概要になります。
- npmでプロジェクトを作成する
- expressやwebpack等をインストールする
- 必要なファイルを手動で作成する
- コマンドラインでWEBサーバー(express)を立ち上げる
- ブラウザでアクセスして確認する
- JSファイルを編集し、ブラウザに反映された結果を確認する
1. npmでプロジェクトを作成する
任意のディレクトリで以下のコマンドを実行します
mkdir express-webpack-hmr-demo
cd express-webpack-hmr-demo
npm init -y
2. expressやwebpack等をインストールする
以下のコマンドを実行します。
npm install -D webpack webpack-cli webpack-dev-middleware webpack-hot-middleware
npm install --save express
npm install --save lodash
3. 必要なファイルを手動で作成する
以下のディレクトリ、ファイルを作成します。
- ./index.html
- ./server.js
- ./webpack.config.js
- ./src/foo.js
- ./src/bar.js
- ./src/index.js
以下のようなディレクトリ構造になるはずです。
│ index.html
│ package.json
│ server.js
│ webpack.config.js
│
├─node_modules
│ └─etc...
└─src
bar.js
foo.js
index.js
./package.jsonを以下のように書き換えます。
- - "main": "index.js",
- + "private": true,
- + "dev": "node server",
{ "name": "express-webpack-demo", "version": "1.0.0", "description": "", "private": true, "scripts": { "dev": "node server", "test": "echo \"Error: no test specified\" && exit 1" }, "keywords": [], "author": "", "license": "MIT", "devDependencies": { "webpack": "^4.29.6", "webpack-cli": "^3.3.0", "webpack-dev-middleware": "^3.6.1", "webpack-hot-middleware": "^2.24.3" }, "dependencies": { "express": "^4.16.4", "lodash": "^4.17.11" } }
./webpack.config.jsの内容を次のようにし、webpackの動作を設定します。
const webpack = require('webpack'); module.exports = { mode: 'development', // HMRするためのモジュールと、自分で作成したファイルの2つをwebpackの基点とする // https://qiita.com/chuck0523/items/caacbf4137642cb175ec#3-entry--文字列-vs-配列-vs-オブジェクト entry: ['webpack-hot-middleware/client', './src/index.js'], output: { // 出力されるファイル名 filename: 'main.js', // 出力されるファイルを読み込めるパスを指定する // ファイルはメモリ上に出力され、そのファイルをロードするためのパス // https://webpack.js.org/configuration/output/#outputpublicpath // https://webpack.js.org/guides/public-path/ publicPath: '/example_public_path/' }, plugins: [ new webpack.HotModuleReplacementPlugin(), ] };
./server.js
WEBサーバー(express)の動作を記述します。
const express = require('express'); const webpack = require('webpack'); const webpackDevMiddleware = require('webpack-dev-middleware'); const webpackHotMiddleware = require('webpack-hot-middleware'); const WebpackConfig = require('./webpack.config'); const app = express(); const compiler = webpack(WebpackConfig); // メモリ上にファイルをコンパイルする // ファイルを監視して、変更されていれば再コンパイルする app.use(webpackDevMiddleware(compiler, { publicPath: WebpackConfig.output.publicPath, })); // クライアントに変更を通知する // クライアント側でHMRに対応している箇所はリロードせずに更新される app.use(webpackHotMiddleware(compiler)); // index.htmlを静的に配置しているディレクトリを指定する app.use(express.static(__dirname)); // サーバーの待ち受けを開始する const port = process.env.PORT || 8080; app.listen(port, () => { console.log(`Server listening on http://localhost:${port}, Ctrl+C to stop`); });
./index.html
<!doctype html> <html> <head> <title>express + webpack</title> </head> <body> <script src="./example_public_path/main.js"></script> </body> </html>
./src/foo.js
export default () => { return 'from foo!'; }
./src/bar.js
export default () => { return 'from bar!'; }
./src/index.js
下部にてHMRを実行するための記述をしています。
ファイルが編集されるとサーバーから変更の通知を受け取り、module.hot.accept()の第1引数で指定したJSが変更されていた場合に第2引数のコールバックが実行されます。
コールバックによってbodyタグを再描画しています。
import _ from 'lodash'; import foo from './foo.js' import bar from './bar.js' const expressAndWebpack = () => { let body = document.querySelector('body'); body.innerHTML = ''; body.innerHTML = _.join(['Hello', 'express', '+', 'webpack'], ' '); body.innerHTML += `<br>${foo()}`; body.innerHTML += `<br>${bar()}`; return body; } expressAndWebpack(); // HMRに対応していれば if (module.hot) { // 第1引数のJSが変更された際に、第2引数のコールバックをブラウザ側で実行する module.hot.accept(['./foo.js', './bar.js'], () => { expressAndWebpack(); }); };
4. コマンドラインでWEBサーバー(express)を立ち上げる
以下のコマンドを実行して、WEBサーバーを立ち上げます。
npm run dev
上記のコマンドは./package.jsonに定義してあり、以下のコマンドと同等です。
node server
5. ブラウザでアクセスして確認する
以下のURLにアクセスします。
http://localhost:8080/
以下のように表示されていれば成功です。
Hello express + webpack
from foo!
from bar!
6. JSファイルを編集し、ブラウザに反映された結果を確認する
WEBサーバーを立ち上げたまま、JSファイルを編集します。
以下のJSファイルを編集して保存します。
./src/foo.js
export default () => { return 'from foo!'; } ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ export default () => { return 'FROM FOO!!'; }
表示が以下のように即座に変更されれば成功です。
Hello express + webpack
from foo!
from bar!
↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
Hello express + webpack
FROM FOO!!
from bar!