JavaScript
Node.js
webpack
webpack-dev-server

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

概要

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-serverwebpackを用いたフロントエンド開発のときに利用できる開発用の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 で起動

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

起動結果

以下のようになる

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

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

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

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の大きな特長の1つはオートリフレッシュ(自動更新)機能である。
コードやリソースに変更があったら自動的に読み直してくれる。

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

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

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

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

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

CLIで実行する場合
webpack-dev-server --inline
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モードで実行する

CLIで実行する場合
webpack-dev-server --no-inline
configに設定する場合(webpack.config.js)
devServer: {
   inline: false
}

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

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

CLIで実行する場合
webpack-dev-server --lazy
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というディレクトリを公開する場合

CLIで実行する場合
webpack-dev-server --content-base public/
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/"と指定すると

webpack.config.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など)の変更を監視する

CLIで実行する場合
webpack-dev-server --watch-content-base
configに設定する場合(webpack.config.js)
devServer: {
   watchContentBase: true
}
  • この設定により、コンテンツベース以下に置かれたファイルに変更があった場合、ブラウザ全体がリロードされる
  • watchContentBaseが省略された場合は、ファイルに変更があっても自動リロードされない

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

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

CLIで実行する場合
webpack-dev-server --hot
  • HMRモードを有効にする

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

CLIで実行する場合
webpack-dev-server --hot-only
  • HMR(Hot Module Replacement)は有効だが、敗した場合には再読込しない

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

  • デフォルト(オプション省略時)はHMRは無効

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

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

CLIで実行する場合
webpack-dev-server --open
configに設定する場合(webpack.config.js)
devServer: {
   open: true
}
  • サーバー起動時に自動的にブラウザを開く(デフォルトのブラウザが開く)

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

CLIで実行する場合
webpack-dev-server --open-page index2.html
configに設定する場合(webpack.config.js)
devServer: {
   open:true,
   openPage:"index2.html"
}
  • サーバー起動時に自動的にブラウザを開き指定したページを開く。
  • CLIの場合には --open-pageの他に --openは指定不要だが、webpack.config.jsで設定する場合には、open:trueを明示的に指定する必要がある。

設定例

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

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"
  }
}

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


    }

};