概要
Webpack 4を利用した開発に便利な webpack-dev-server の設定メモです。
webpack-dev-serverのソースコードと公式wiki、公式Docを参考に実動確認した結果を書きます。
HMR等オートリロード系の設定(--hot
とかとか--inline
とか)やよく使いそうなCLIオプションなど理解しておくためのメモです。
環境
本投稿で使っている環境は以下の通り。
- webpack-dev-server:3.1.7
- webpack:4.17.1
- webpack-cli:3.1.0
webpack-dev-server とは
webpack-dev-serverはwebpackを用いたフロントエンド開発のときに利用できる開発用のwebサーバー。
*.jsファイルやリソースファイルを更新すると即座に反映してくれる機能をもっている。
webpack-dev-serverの後継がwebpack-serveになってwebpack-dev-serverはメンテナンスモードに入るという話もあったが方針が変わり、今後もwebpack-dev-serverは開発継続される模様。
インストール
通常、グローバルではなくローカルインストールする
npm install webpack-dev-server --save-dev
いちばんシンプルな起動
以下のように、特別な設定などを行っていないまっさらな状態で起動してwebpack-dev-serverのデフォルトの挙動を確認する。
[project_dir]
|-package.json
|-src
| |-index.js
package.jsonのscriptsに以下のように記述して npm start
で起動
"scripts": {
"start": "webpack-dev-server"
}
起動結果
以下のようになる
- エントリポイントは ./src/index.js
- コンテンツのルート(content-base)はプロジェクトのワークディレクトリの直下 /
- ホストは http://localhost:8080
- バンドルは http://localhost:8080/main.js
一応起動はできるが、このままだとほぼ使えないので各種設定を行う。
package.json(CLI実行)の設定例
以下のようにwebpack-dev-serverを起動オプションつきで起動することで様々な設定をすることができる
"scripts": {
"start": "webpack-dev-server --hot --inline --watch-content-base --content-base public/ --open-page index.html ",
.......
},
詳しい設定内容は後述するが、各オプションの意味は以下のとおり
-
--hot
でHMR(Hot Module Replacement)を有効にする -
--inline
でjsコードが変更されたら(コンパイルして)自動的にブラウザをリロードする。ブラウザ自動リロードのための仕組みがinlineモードで実行される。 -
--content-base public/
でhtmlやcssなどを置いておくコンテンツベースとなるディレクトリを**[作業ディレクトリ]/public/**に指定する -
--watch-content-base
で、htmlやcssなどファイルが変更されたらブラウザを自動的にリロードする -
--open-page index.html
で、サーバー起動後、自動的に(デフォルトの)ブラウザを開き、指定されたページここではindex.htmlを開く -
npm start
でwebpack-dev-serverを起動する
設定と各種挙動
通常の開発ではwebpack-dev-serverに各種起動オプションを指定して挙動を制御するが、何をどう設定すると、どう動くのか、デフォルト(省略時)はどうなのかという点を意識して代表的な設定をまとめる。
ブラウザの自動リロード
webpack-dev-serverの大きな特長の1つはオートリフレッシュ(自動更新)機能である。
コードやリソースに変更があったら自動的に読み直してくれる。
混同しがちなのはオートリフレッシュ機能とHMR(Hot Module Replacement)機能。
webpack-dev-serverはデフォルトだとオートリフレッシュ機能により、コードに変更があった場合、自動的にブラウザ全体をリロードしてくれる。ブラウザ全体をリロードするのでフォームに入力していた値などはクリアされてしまう。
そこで、ブラウザ全体をリロードするのではなく、変更したモジュール(jsコード)だけを置き換えてくれるのがのがHMR(Hot Module Replacement)という機能だが、この機能を使うためにはこのサーバー側の設定だけではなく、アプリ側もHMRのお作法で設計する必要がある。(reactなど有名フレームワークはこの機能に対応しているものがある)
(1)オートリフレッシュ(自動再読込)の設定
(1)-1 オートリフレッシュ(自動再読込)をinlineモードで実行する
webpack-dev-server --inline
devServer: {
inline: true
}
- --inlineは省略可能。省略してもデフォルトでinlineモードになる
- この設定では、オートリフレッシュを実現するためのコードがバンドル(最終jsコード)の中にwebpack-dev-serverによって自動的に追加される。つまりdist/main.jsとしてアクセスできるバンドル内にオートリフレッシュのためのコードが組み込まれた状態になる。
- 次の項(1)-2で説明する iframe モードで使いたい場合は、inlineモードでもURLに /webpack-dev-server をつけてアクセスする(例 http://localhost:8080/webpack-dev-server/ )ことでiframeモードでアクセスできる
(1)-2 オートリフレッシュ(自動再読込)をiframeモードで実行する
webpack-dev-server --no-inline
devServer: {
inline: false
}
iframeの中でアプリを動作させるモード。
実行すると画面上部には実行ステータスが表示される。
(1)-3 オートリフレッシュ(自動再読込)を無効にする。
webpack-dev-server --lazy
devServer: {
lazy: true
}
- オートリフレッシュが無効の場合、手動でブラウザのリロードすればリコンパイルされる。
watchOptionsも無効になる -
--hot
オプションとは併用できない
(2)リソース・コンテンツ(htmlファイルなど)と自動読み込み
webpack-dev-serverはwebサーバーでもあるので当然ながらhtmlやcssもホストすることができる。
公開するディレクトリをコンテンツベース(content base)という。
また、jsコードだけでなくhtmlなどリソースファイルを監視(watch)し、それらが変更された場合に、自動的にリロードされるように設定できる。
(2)-1 コンテンツベース(公開するリソースのルートとなるディレクトリ)の指定
[作業dir]
|― src
|― public
となっているときに**/public**というディレクトリを公開する場合
webpack-dev-server --content-base public/
devServer: {
contentBase: path.join(__dirname, 'public')
}
(2)-2 バンドルにアクセスするためのpublicPathの指定
webpack-dev-serverによってビルドされたバンドル(js)は、このpublicPathで指定したパスでアクセスできる。
以下のようなフォルダ構成のときバンドル
[作業dir]
|― src
|― index.js
|― foo.js
|― bar.js
|― public
|― index.html
src以下にあるソースjsファイルを結合した(コンパイル、マージなどなど)バンドルはデフォルトでは
http://localhost:8080/main.js としてブラウザからアクセスできる。
例えば、webpack.config.jsのpublicPathを以下のように **"/js/"**と指定すると
module.exports = {
//...
output: {
//...
publicPath: "/js/",
},
}
http://localhost:8080/js/main.js としてブラウザからアクセスできる(※)。
※バンドルはメモリ上にありファイルとしての実態は無い
webpack-dev-serverで生成された**バンドルはオンメモリで保持される。そのためバンドルがファイルとして保存されることは無い**。(main.jsなどファイルとしての実体はみつからない。)
また、webpackでビルドするとwebpack.config.jsのoutput.path
で指定したディレクトリにバンドルが出力されるが、webpack-dev-serverから同名のファイルにアクセスしようとした場合、デフォルトではメモリ上のバンドルが使われる。
(2)-3 コンテンツベースに置かれたファイル(htmlやcssなど)の変更を監視する
webpack-dev-server --watch-content-base
devServer: {
watchContentBase: true
}
- この設定により、コンテンツベース以下に置かれたファイルに変更があった場合、ブラウザ全体がリロードされる
- watchContentBaseが省略された場合は、ファイルに変更があっても自動リロードされない
(3)HMR(Hot Module Replacement)の設定
(3)-1 HMR(Hot Module Replacement)を有効にする
webpack-dev-server --hot
- HMRモードを有効にする
(3)-2 HMRに失敗した場合には再読込しない設定にする
webpack-dev-server --hot-only
- HMR(Hot Module Replacement)は有効だが、敗した場合には再読込しない
(3)-3 HMR(Hot Module Replacement)を無効にする
- デフォルト(オプション省略時)はHMRは無効
(4)ブラウザを自動的に起動する
(4)-1 サーバー起動時にブラウザを自動的に起動する
webpack-dev-server --open
devServer: {
open: true
}
- サーバー起動時に自動的にブラウザを開く(デフォルトのブラウザが開く)
(4)-2 サーバー起動時に指定したページを自動的に起動する
webpack-dev-server --open-page index2.html
devServer: {
open:true,
openPage:"index2.html"
}
- サーバー起動時に自動的にブラウザを開き指定したページを開く。
- CLIの場合には
--open-page
の他に--open
は指定不要だが、webpack.config.jsで設定する場合には、open:true
を明示的に指定する必要がある。
アクセスすると Invalid Host header が出る場合
virtual boxなどの仮想化環境や、ngrokなどでローカルホスト外から使った場合 webpack-dev-serverでホストしているページにアクセスすると Invalid Host headerが出る場合がある。
その場合は以下のようにする。
devServer: {
host: '0.0.0.0',
disableHostCheck: true
}
設定例
設定はCLIからもwebpack.config.jsでもできるが、シンプルな設定例(webpack.config.js)
{
"name": "webpack-dev-server-tutorial",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"start": "webpack-dev-server",
"build": "webpack --config webpack.config.js"
},
"author": "",
"license": "MIT",
"devDependencies": {
"webpack": "^4.17.1",
"webpack-cli": "^3.1.0",
"webpack-dev-server": "^3.1.7"
}
}
const path = require("path");
module.exports = {
mode: 'development',//webpack4以降はモード指定しなければいけない
entry: {app: './src/index.js'},//エントリーポイント。連想配列にすることでappというキーに対してはindex.jsがentryとセットできる
output: {
path: path.join(__dirname, "public"),
publicPath: "/js/", //ブラウザからバンドルにアクセスする際のパス
filename: '[name].js', //バンドルのファイル名。[name]の部分にはentryで指定したキーが入る
library: ["com", "example"], //パッケージ名を配列で表現する
libraryTarget: 'umd'
},
devtool: 'inline-source-map',//ブラウザでのデバッグ用にソースマップを出力する
//webpack-dev-server用設定
devServer: {
open: true,//ブラウザを自動で開く
openPage: "index.html",//自動で指定したページを開く
contentBase: path.join(__dirname, 'public'),// HTML等コンテンツのルートディレクトリ
watchContentBase: true,//コンテンツの変更監視をする
port: 3000, // ポート番号
}
};