webpack4対応webpack-dev-serverの主要な設定オプション(CLI,webpack.config.js)の意味と挙動

# 概要
Webpack 4を利用した開発に便利な webpack-dev-server の設定メモです。
**webpack-dev-server**の[ソースコード](https://github.com/webpack/webpack-dev-server)と[公式wiki](https://github.com/webpack/docs/wiki/webpack-dev-server)、[公式Doc](https://webpack.js.org/configuration/dev-server/)を参考に実動確認した結果を書きます。

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](https://github.com/webpack/webpack-dev-server)は[webpack](https://webpack.js.org/)を用いたフロントエンド開発のときに利用できる開発用のwebサーバー。
**\*.js**ファイルやリソースファイルを**更新すると即座に反映してくれる**機能をもっている。

**webpack-dev-server**の後継が[webpack-serve](https://github.com/webpack-contrib/webpack-serve)になって**webpack-dev-server**はメンテナンスモードに入るという話もあったが方針が変わり、今後も**webpack-dev-server**は[開発継続される模様](https://qiita.com/riversun/items/52e2d8d95812c209502c)。

# インストール

通常、グローバルではなくローカルインストールする

```shell
npm install webpack-dev-server --save-dev
```

# いちばんシンプルな起動

以下のように、特別な設定などを行っていないまっさらな状態で起動して**webpack-dev-server**のデフォルトの挙動を確認する。

```shell:何も設定していない状態のディレクトリ
[project_dir]
 |-package.json
 |-src
 |  |-index.js
```


**package.json**のscriptsに以下のように記述して ```npm start``` で起動

```json:package:json
"scripts": {
  "start": "webpack-dev-server"
}
```

### 起動結果
以下のようになる

- エントリポイントは **./src/index.js**
- コンテンツのルート(content-base)はプロジェクトのワークディレクトリの直下 **/** 
- ホストは **http://localhost:8080**
- バンドルは **http://localhost:8080/main.js** 

一応起動はできるが、このままだとほぼ使えないので各種設定を行う。

# package.json(CLI実行)の設定例

以下のようにwebpack-dev-serverを起動オプションつきで起動することで様々な設定をすることができる

```shell:package.json　(CLIで実行する場合)
  "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の大きな特長の１つはオートリフレッシュ(自動更新)機能である。
コードやリソースに変更があったら自動的に読み直してくれる。

混同しがちなのはオートリフレッシュ機能とHMR(Hot Module Replacement)機能。

webpack-dev-serverはデフォルトだとオートリフレッシュ機能により、コードに変更があった場合、自動的にブラウザ全体をリロードしてくれる。ブラウザ全体をリロードするのでフォームに入力していた値などはクリアされてしまう。

そこで、ブラウザ全体をリロードするのではなく、変更したモジュール（jsコード）だけを置き換えてくれるのがのが**HMR**(Hot Module Replacement)という機能だが、この機能を使うためにはこのサーバー側の設定だけではなく、アプリ側もHMRのお作法で設計する必要がある。（reactなど有名フレームワークはこの機能に対応しているものがある）

# (1)オートリフレッシュ(自動再読込)の設定

### (1)-1 オートリフレッシュ(自動再読込)をinlineモードで実行する

```shell:CLIで実行する場合
webpack-dev-server --inline
```

```js:configに設定する場合(webpack.config.js)
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モードで実行する

```shell:CLIで実行する場合
webpack-dev-server --no-inline
```

```js:configに設定する場合(webpack.config.js)
devServer: {
   inline: false
}
```

iframeの中でアプリを動作させるモード。
実行すると画面上部には実行ステータスが表示される。

### (1)-3 オートリフレッシュ(自動再読込)を無効にする。

```shell:CLIで実行する場合
webpack-dev-server --lazy
```

```js:configに設定する場合(webpack.config.js)
devServer: {
   lazy: true
}
```

- オートリフレッシュが無効の場合、手動でブラウザのリロードすればリコンパイルされる。
watchOptionsも無効になる　
- ```--hot```オプションとは併用できない

# (2)リソース・コンテンツ(htmlファイルなど)と自動読み込み

webpack-dev-serverはwebサーバーでもあるので当然ながらhtmlやcssもホストすることができる。
公開するディレクトリをコンテンツベース(content base)という。

また、jsコードだけでなくhtmlなどリソースファイルを監視(watch)し、それらが変更された場合に、自動的にリロードされるように設定できる。

### (2)-1 コンテンツベース(公開するリソースのルートとなるディレクトリ)の指定

```
[作業dir]
|― src
|― public
```

となっているときに**/public**というディレクトリを公開する場合

```shell:CLIで実行する場合
webpack-dev-server --content-base public/
```

```js:configに設定する場合(webpack.config.js)
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/"**と指定すると

```js:webpack.config.js
module.exports = {
    //...
    output: {
        //...
        publicPath: "/js/",
    },
}
```

**http://localhost:8080/js/main.js** としてブラウザからアクセスできる（※）。

### ※バンドルはメモリ上にありファイルとしての実態は無い

webpack-dev-serverで生成された**<font color="red">バンドルはオンメモリで保持される</font>**。そのためバンドルが**<font color="red">ファイルとして保存されることは無い</font>**。（**main.js**などファイルとしての実体はみつからない。）

また、webpackでビルドするとwebpack.config.jsの```output.path```で指定したディレクトリにバンドルが出力されるが、webpack-dev-serverから同名のファイルにアクセスしようとした場合、デフォルトではメモリ上のバンドルが使われる。


### (2)-3 コンテンツベースに置かれたファイル(htmlやcssなど)の変更を監視する

```shell:CLIで実行する場合
webpack-dev-server --watch-content-base
```

```js:configに設定する場合(webpack.config.js)
devServer: {
   watchContentBase: true
}
```

- この設定により、コンテンツベース以下に置かれたファイルに変更があった場合、ブラウザ全体がリロードされる
- watchContentBaseが省略された場合は、ファイルに変更があっても自動リロードされない

# (3)HMR(Hot Module Replacement)の設定


### (3)-1 HMR(Hot Module Replacement)を有効にする

```shell:CLIで実行する場合
webpack-dev-server --hot
```

- HMRモードを有効にする

### (3)-2 HMRに失敗した場合には再読込しない設定にする

```shell:CLIで実行する場合
webpack-dev-server --hot-only
```

- HMR(Hot Module Replacement)は有効だが、敗した場合には再読込しない

### (3)-3 HMR(Hot Module Replacement)を無効にする

- デフォルト（オプション省略時）はHMRは無効


# (4)ブラウザを自動的に起動する

### (4)-1 サーバー起動時にブラウザを自動的に起動する

```shell:CLIで実行する場合
webpack-dev-server --open
```

```js:configに設定する場合(webpack.config.js)
devServer: {
   open: true
}
```

- サーバー起動時に自動的にブラウザを開く（デフォルトのブラウザが開く)


### (4)-2 サーバー起動時に指定したページを自動的に起動する

```shell:CLIで実行する場合
webpack-dev-server --open-page index2.html
```

```js:configに設定する場合(webpack.config.js)
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**が出る場合がある。

その場合は以下のようにする。
      

```js:Invalid　Host　header　対策
devServer: {
   host: '0.0.0.0',
   disableHostCheck: true
}
```

### 設定例

設定はCLIからもwebpack.config.jsでもできるが、シンプルな設定例(webpack.config.js)

```json:package.json
{
  "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"
  }
}

```

```js:webpack.config.js

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, // ポート番号


    }

};
```