💻

webpack-dev-server のオプション全部試した

2020/10/19に公開

はじめに

webpack-dev-serverは、webpackを用いたプロジェクトにおける開発用サーバで、ブラウザのライブリローディングを提供します。

それによって、ソースコードの編集時に、webpackによる差分ビルドが実行されると、ブラウザがwebpack-dev-server経由で更新差分を受け取り、リアルタイムに変更内容を確認しながら開発ができるようになります。

webpack-dev-serverは内部的にはexpressで実装されており、express用のミドルウェアである webpack-dev-middleware によって実現されています。webpack-dev-serverに渡せるオプションの多くは、webpack-dev-middlewareにそのまま渡すオプションのため、この関係性を理解しておくと捗りそうです。

本記事では、ドキュメント から確認できるwebpackにおけるwebpack-dev-server用の設定を、可能な限り全て検証します。あくまで可能な限りなので、全然わからんってなったものは飛ばします。

各パッケージのバージョンは以下package.jsonに従います。

{
  "name": "webpack-dev-server-test",
  "version": "1.0.0",
  "main": "index.js",
  "license": "MIT",
  "devDependencies": {
    "webpack": "^4.44.1",
    "webpack-cli": "^3.3.12",
    "webpack-dev-server": "^3.11.0"
  }
}

また、ベースとなる設定ファイルは以下のものになります。

module.exports = {
  mode: 'development',
  entry: `${__dirname}/src/main.js`,
  output: {
    path: `${__dirname}/public`,
    filename: 'bundle.js'
  },

  devServer: {
    // ここを書き換えてく
  }
}

文字数の都合上、以降ではdevServerの部分だけ抽出して記載します。

設定なし

devServer: {
}

設定を特に記述しない状態でも、webpack-dev-serverは立ち上がります。

この状態で localhost:8080 にアクセスすると、 expressで動いている開発用サーバにアクセスすることができますが、HMRのような実用的な使い方はまだできません。

hot

devServer: {
  hot: true
}

HMR(Hot Module Replacement) を有効にします。

HMR は、ソースコードを一部修正した場合に、ブラウザをリロードさせずに、変更したファイルに関連するモジュールのみをブラウザが動的に読み込みこませる仕組みです。

これによってブラウザを一々リロードさせずに、最低限の再描画だけでコーディングと検証を同時に行えるようになります。

hotOnly

devServer: {
  hot: true,
  hotOnly: true
}

通常、HMR は失敗時にブラウザをリロードするようフォールバックしますが、それを抑制することができます。

host/port

devServer: {
  host: '0.0.0.0',
  port: 3001
}

webpack-dev-serverを動かすホストとポートを指定できます。デフォルトの8080は開発環境としてよく使われるポートなので、APIサーバなどが8080で別途動いているなどの場合は、書き換えてあげる必要があります。

publicPath

  devServer: {
    contentBase: path.join(__dirname, 'public'),
    publicPath: '/assets/'
  }

webpackによってバンドルされたファイルがどこに配置されているかを、webpack-dev-serverに伝えます。

webpack-dev-serverはバンドルをオンメモリで持つので、実際にバンドルファイルが作成されることはありませんが、ソースコード間の依存関係を解決する際に、ファイルが生成されている前提でファイルパスが記述されているため、それを解決するために伝える必要があります。

そのため、通常はwebpack側で設定しているoutput.publicPathにそろえておけば問題ありません。

contentBase

devServer: {
  contentBase: path.join(__dirname, 'public')
}

静的ファイルを配布するディレクトリを指定します。今回は public ディレクトリに index.html を配置し、そこからバンドルファイルを読み込む構成で検証しているため、このように設定することで、webpack-dev-serverが立ち上がっているポートにアクセスすると、HTMLファイルが読み込まれます。

contentBasePublicPath

devServer: {
  contentBase: path.join(__dirname, 'assets'),
  contentBasePublicPath: '/public'
}

contentBase で提供する静的ファイルを参照するためのパスを指定します。
やや小難しい話ですが、上記の例では、assets/images/hoge.png に配置された静的ファイルを、 public/images/hoge.png でリクエストすることで取得できるようになります。

デフォルトでは / になるため、クライアント側はassets のような物理パスを指定せずとも静的ファイルを取得できていたわけです。

index

devServer: {
  contentBase: path.join(__dirname, 'public'),
  index: 'hoge.html'
}

インデックスファイルのファイル名を変更します。

のはずなんだけど、何故か手元で意図通りに動かない。

以下のように、historyApiFailback のオプションの方でインデックスファイルを指定するとうまく動くんですが…。

devServer: {
  contentBase: path.join(__dirname, 'public'),
  historyApiFallback: {
    index: 'hoge.html'
  }
}

serveIndex

devServer: {
  serveIndex: false
}

trueにすると、/ にアクセスした際に、ディレクトリ一覧ページ(インデックスページ)を返します。index.html が存在する場合はそちらが優先されます。

compress

devServer: {
  contentBase: path.join(__dirname, 'public'),
  compress: true
}

webpack-dev-serverが提供するコンテンツをgzip化するかどうかを指定できます。

disableHostCheck(非推奨)

iPhoneなど、同一ネットワーク上のほか端末から、PC上で起動してるwebpack-dev-serverへのアクセスを実現するために、ホスト名のチェックを無視させます。
現在はセキュリティの都合非推奨となっているので、後述の useLocalIp を用いること。

参考
[https://dev.to/origamium/webpack-dev-server-disablehostcheck-4am:embed:cite]

useLocalIp

devServer: {
  host: '0.0.0.0',
  useLocalIp: true
}

ローカルIPアドレスを使ってブラウザを開くようにします。

allowedHosts

devServer: {
  contentBase: path.join(__dirname, 'public'),
  allowedHosts: [
    'host.com',
    'subdomain.host.com',
    'subdomain2.host.com',
    'host2.com'
  ]
}

webpack-dev-server にアクセスできるホストを制限します。リモート開発環境とか作るときに使うのかな。。

headers

devServer: {
  contentBase: path.join(__dirname, 'public'),
  headers: {
    'X-Custom-Foo': 'bar'
  }
}

webpack-dev-server からの全てのレスポンスに任意のレスポンスヘッダーを含めます。
'Access-Control-Allow-Origin': '*' を付与し、開発環境上でのCORSを実現するようなときに使えます。

writeToDisk

devServer: {
  writeToDisk: true
}

通常、webpack-dev-serverはビルド後のバンドルをオンメモリで持つためファイル出力を行いませんが、writeToDiskオプションを指定することで、あえてファイル出力を行わせることができます。

inline

devServer: {
  contentBase: path.join(__dirname, 'public'),
  inline: false
}

HMRの方法をinlineモードかiframeモードどちらを使うかを指定します。

inlineモード(inline: true): ブラウザコンソール上に進捗やエラーが表示される

[f:id:shingo-sasaki-0529:20200802004041p:plain]

iframeモード(inline: false): 進捗やエラーが表示される領域と、iframe内にアプリケーション本体を表示するエントリーポイントが用意される

[f:id:shingo-sasaki-0529:20200802003813p:plain]

historyApiFallback

devServer: {
  historyApiFallback: true
}

存在しないパスをリクエストされた場合に、404を返さずにインデックファイルを戻すようにします。

フロントエンドが HTML5 History API を用いて、物理パスは存在しないけど論理パスを設定することによってSPAを実現しているような場合、webpack-dev-serverに対して、物理パスが存在しないリクエストが飛ぶような場合に使用します。インデックスページを受けた取ったブラウザが、論理パスを元にフロントエンド側でルーティングを行うことでSPAを実現します。

before / after

devServer: {
  contentBase: path.join(__dirname, 'public'),
  before (app, server, compiler) {
    console.log('びふぉー')
  },
  after (app, server, compiler) {
    console.log('あふたー')
  }
}

webpack-dev-server が提供する全てのミドルウェアの実行前(before)と実行後(after)に、カスタムミドルウェアを注入することができます。

ここで言うミドルウェアは、express がリクエストを処理する際に注入できるフックなので、より高度なカスタマイズをするときに使えます。

onListening

devServer: {
  onListening: function(server) {
    const port = server.listeningApp.address().port
    console.log('Listening on port:', port)
  }
}

webpack-dev-server が特定ポートでリッスン開始したタイミングで、任意の関数を実行することができます

overlay

devServer: {
  overlay: true
}

ビルド時にエラーないし警告が発生した際に、アプリケーションの画面上にオーバーレイでメッセージが表示されるようになります。
[f:id:shingo-sasaki-0529:20200802181607p:plain]

watchContentBase

devServer: {
  contentBase: path.join(__dirname, 'public'),
  watchContentBase: true
}

contentBase で指定された静的ファイルに対しても変更監視を行い、変更を検知した場合にライブリロードします

http2

devServer: {
  http2: true
}

HTTP/2を用います。が、Node 10.0.0 からはnode側の組み込み HTTP/2 を用いるため基本的には設定不要です

https

devServer: {
  https: true
}

webpack-dev-server はデフォルトではHTTPを用いますが、あえてHTTPSを使うことができます。また、上記設定の場合はオレオレ証明書を使用しますが、明示的に指定することも可能です。

lazy / filename

devServer: {
  lazy: true,
  filename: `${__dirname}/assets/bundle.js`
}

lazy: true と設定することで、遅延モードでwebpack-dev-serverを動かします。遅延モードではファイルの監視を行わずに、リクエストが来たタイミングでバンドルの生成を行うモードです。

バンドルの生成を行うため、 filename に指定も必要になります。

デフォルトで output.filenmae のほうで指定したファイル名が入るのでそれで充分かと思いましたが、上記設定のように絶対パスで指定しないと上手く動きませんでした(何故?)

liveReload

devServer: {
  liveReload: false
}

falseにすることで、ブラウザのライブリロードを無効にします

mimeTypes

devServer: {
  contentBase: path.join(__dirname, 'public'),
  mimeTypes: { 'text/html': ['phtml'] }
}

レスポンスのContent-Typeをカスタマイズすることができます。(インラインモードでは使えない…??)

proxy

devServer: {
  port: 8080,
  proxy: {
    '/api': 'http://localhost:3000'
  }
}

任意のパスを別のサーバへプロキシさせることができます。プロキシ時にパスをリライトしたり、セキュリティの設定を付与するなどの細かい設定も可能です。

上記の例の場合、3000ポートでRailsなどのAPIサーバが立ち上がっているとき、APIへのリクエストもwebpack-dev-serverが動いている8080に対して投げても、Railsサーバ側にプロキシしてくれるわけです。

staticOptions

devServer: {
  contentBase: path.join(__dirname, 'public'),
  staticOptions: {
    maxAge: 3 * 24 * 60 * 60 * 1000
  }
}

contentBase によって指定された、静的ファイルを配信する際のオプションを指定できます。

オプションは express.static のオプションに基づくため、上記の場合はpublicから配信される静的ファイルのmax-ageが3日間(259200ms)になります。

clientLogLevel

devServer: {
  clientLogLevel: 'silent'
}

ブラウザコンソール上に表示するログレベルを変更できます。

noInfo

devServer: {
  noInfo: true
}

ビルドに関するログの出力を抑制します

quiet

devServer: {
  quiet: true
}

初回起動時以降の、コンソールへのログを全て無効化します。infoに限らず、エラー系も全て表示されなくなるので注意

stats

devServer: {
  stats: 'normal'
}

webpackによるバンドル情報の出力レベルを設定します

watchOptions

devServer: {
  watchOptions: {
    poll: true
  }
}

ファイルの変更監視の方法を変更します。webpack-dev-serverでは通常、ファイルシステムを使って変更を検知するけど、NFSやvagrantを使っていると上手く行かないらしく、そういう場合に poll: true とすると変更をポーリングで検知できるようになるようです。

open

devServer: {
  contentBase: path.join(__dirname, 'public'),
  open: true
}

初回ビルド完了時にブラウザでインデックスファイルを開いてくれます。

openPage

devServer: {
  contentBase: path.join(__dirname, 'public'),
  open: true,
  openPage: 'hoge.html'
}

open と組み合わせて、ブラウザで開くファイルを指定することができます。

socket / sockHost / sockPath / sockPort

devServer: {
  socket: 'socket',
  sockHost: 'myhost.test',
  sockPath: '/socket',
  sockPort: 8080
}

HTTPを使わずにソケット通信でwebpack-dev-serverにアクセスするための設定です。
※ 動作未確認

bonjour

devServer: {
  bonjour: true
}

ゼロコンフィギュレーションネットワークのためのBonjour を有効化します。正直よくわからない。

よくわからなかった奴ら

  • pfx
  • pfxPassphrase
  • injectClient
  • injectHot

上記はドキュメントを読んだり、試したり軽く調べた限りでもよくわかりませんでした。
分かり次第追記しますが、具体例のある解説などを頂けたら幸いです。

GitHubで編集を提案

Discussion