前々回に モダンなフロントエンド開発環境をつくる という記事を書きましたが、その中で使った WebPack-Dev-Server を捨てたのでその背景と、Step-by-Stepの導入フローを書いてみます。
Contents
WebPack-Dev-Server を捨てた理由
大きく次の問題がありました。
- Scssで@importされているファイルを変更しても、正しくビルドされない
- TypeScriptのimportは問題ない
- ビルド時にマップファイルが生成されず、バグが発生したときにそのまま調査に入れない
- ビルド結果がインメモリでサーブされるため、ビルドされたソースを確認するには別途 webpack コマンドを打たなくてはいけない
特に1番目のものがクリティカルで、CSSを適用する段階では毎回webpackコマンドを打って実際にビルドしなくてはならず、使い物にならなかったためです。
この3つの問題をすべて解決できるのが WebPack + Browsersync です。
Browsersyncとは何か
Browsersyncはファイル変更を監視し、自動でブラウザリロードを行ってくれるツールです。
タスクランナーと合わせてLive Reloadの機能を提供してくれるnpmモジュールは他にもありますが、Browsersyncの特徴の一つはそれらに依存せず、単体で動作する点が挙げられるでしょう。例えばgulpであればgulp-webserver、前回のwebpackであればDev-Serverなどがありますが、これらと違い完全に独立したモジュールとなっています。
また大きな特徴として、複数環境でのデバッグ容易性向上に関する機能が多く含まれているという点です。
例えばFireFoxとChromeで同じページを開いておきながら、片方のブラウザでページ内をクリックしたとき、もう片方のブラウザでも同じ箇所へのクリックイベントを実行することが可能です。スクロールも同期することが可能なので、複数のブラウザを開いていても常に同じ箇所を表示していることになり、同時デバッグやブラウザ間の表示比較などがとても容易に実現できるはずです。(今回はこの機能については触れていません。)
BrowserSyncのインストール
前提として省略していますが、node.jsとnpmを先にインストールする必要があります。
今回もグローバルでインストールします。ローカルでインストールして、パスを通してもOKです。
1 2 3 |
$ npm install -g browser-sync $ browser-sync --version 2.11.1 |
設定ファイルを作る
BrowserSyncは引数でもたくさんのオプションを設定できるのですが、WebPack等と同様に、設定ファイルを作ってロードさせることが可能です。コマンドがすっきりするので、ここでは設定ファイルを利用することにします。
テンプレートの作成
initコマンドで設定ファイルを作ります。
1 2 3 |
$ browser-sync init [BS] Config file created bs-config.js [BS] To use it, in the same directory run: browser-sync start --config bs-config.js |
設定ファイルを変更
BrowserSyncの設定は多数の設定がありますので、それぞれの設定については公式サイトの Browsersync options をご覧ください。
ここでは私好みにカスタムしたものを、デフォルトの設定から変更した箇所だけをハイライトしておきます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
/* ..(省略).. */ module.exports = { "ui": { "port": 3001, "weinre": { "port": 8080 } }, + "files": "./webroot/**/*", "watchOptions": {}, + "server": { + "baseDir": "./webroot", + "middleware": function(req, res, next){ + var timestamp = "[" + new Date().toISOString().replace(/T/, ' ').replace(/\..+/, '') + "] "; + console.log(timestamp + req.method + " " + req.originalUrl + " - " + req.connection.remoteAddress + " - " + req.headers['user-agent']); + next(); + } + }, "proxy": false, "port": 3000, "middleware": false, "serveStatic": [], "ghostMode": { "clicks": true, "scroll": true, "forms": { "submit": true, "inputs": true, "toggles": true } }, "logLevel": "info", + "logPrefix": "MyProj", "logConnections": true, "logFileChanges": true, "logSnippet": true, "rewriteRules": false, "open": "local", + "browser": ["google chrome"], "xip": false, "hostnameSuffix": false, "reloadOnRestart": false, "notify": true, "scrollProportionally": true, + "scrollThrottle": 100, "scrollRestoreTechnique": "window.name", ..(省略).. }; |
この中のポイントは files と server で、これらを適切に設定すればうまく動作します。
files は監視対象のファイルであり、パターンマッチを使用して webroot 以下のすべてのファイルを指定しています。
server に baseDir を設定すれば、BrowserSyncは設定したディレクトリをドキュメントルートとしてコンテンツをサーブします。 middleware ではExpressの形式でreq, resが渡されるので、任意の処理を間に挟むことができます。ここでは簡易的なアクセスログを出力するために設定しています。
設定ファイルを読み込む
設定できたら次のコマンドで試しに実行してみます。
1 |
$ browser-sync start --config bs-config.js |
実行すると、設定ファイルで設定したブラウザで、BrowserSyncがサーブしているページが開かれると思います。
問題なく表示できたら、jsやcssを直接変更してみて自動でページが更新されるかテストしてください。
WebPackと連携
といっても、WebPackのwatch機能を使うだけです。
これまでの構成でも、ターミナルを2つ開いて、片方で webpack --watch -d
を、もう一方で上記のBrowserSyncをスタートさせることで、WebPackでビルドが完了した時点でBrowserSyncによってページを自動更新する構成が可能です。
2つコンソールを開いておかなければいけないのは億劫なのと、できるだけ覚えることは減らした方が良いので、npm script にしましょう。
package.jsonを次のように変更します。
1 2 3 4 5 6 7 8 9 |
{ ..(省略).. "scripts": { + "start": "webpack --watch -d & browser-sync start --config bs-config.js", - "server": "webpack-dev-server --content-base ./webroot/ --inline", "test": "echo \"Error: no test specified\" && exit 1" }, ..(省略).. } |
一応、npmにbrowser-sync-webpack-pluginというWebPack用のBrowserSyncプラグインがあります。ただ、前回の記事の設定のように複数のビルド設定をしている場合、JSとCSSどちらもカバーできるようにうまく設定するのが難しそうな気がしたので試していません。一つのビルドだけで完結するのであれば、設定も容易そうですし試す価値あると思います。
動かしてみる
npm scriptを設定したら、 npm start
コマンドで webpack と browser-sync が同時に実行されるようになります。試してみましょう。
下の出力は、npm startを実行してからscssファイルの変更を行い、ビルドが実行されたことを示す出力です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
$ npm start Hash: 7a271a3ad2c03faf9f32 Version: webpack 1.12.14 Time: 1083ms Asset Size Chunks Chunk Names app.css 7.75 kB 0, 0 [emitted] app, app app.css.map 84 bytes 0, 0 [emitted] app, app + 5 hidden modules Child extract-text-webpack-plugin: + 2 hidden modules [MyProj] File changed: webroot/styles/app.css [MyProj] File changed: webroot/styles/app.css.map [2016-03-16 22:35:04] GET /styles/app.css?rel=1458156914168 - ::1 - Mozilla/5.0... [2016-03-16 22:35:04] GET / - ::1 - Mozilla/5.0... |
最後4行がBrowserSyncによって出力されたログですね。興味深いのは最後の2行で、ページリロードではなく再ビルドされたcssが先行ロードされています。
ブラウザも同時にリロードされ、ちゃんとScssのファイル変更でも自動リロードでできて、マップファイルも毎回生成されていることが分かります。
所感
WebPackとBrowserSyncを組み合わせることで、冒頭で問題としていた WebPack-Dev-Server を使っていたときの課題がすべて解決できました。
BrowserSyncはここで取り組んだ以外にも機能が豊富で、特に複数ブラウザで同時に動作確認を行うには無くてはならないような環境を提供してくれます。
興味があれば、最近リニューアルされたBrowserSyncの公式ページを読んで試してみてください。