webpack-dev-server のオプション全部試した
はじめに
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
上記はドキュメントを読んだり、試したり軽く調べた限りでもよくわかりませんでした。
分かり次第追記しますが、具体例のある解説などを頂けたら幸いです。
Discussion