webpackによるビルドを高速化するための設定がいくつかあるのでそれらの効果を(雑にですが)測ってみました。
なお、ビルド時間の測定には手前味噌ですが、React.js用のGridコンポーネントであるGriddleをBootstrapデザインにするgriddle-react-bootstrapのexamplesアプリを使用しています(動いている物はこちらから見れます)。使用ライブラリは下記のpackage.json
の通り。TypeScriptで書いているので、webpack + ts-loaderでビルドしています。Babelは使用していませんが傾向はたぶん同じになるかと思います。
"devDependencies": {
"cross-env": "^1.0.7",
"ts-loader": "^0.7.2",
"typescript": "^1.8.2",
"webpack": "^1.12.11"
},
"typings": "lib/index.d.ts",
"dependencies": {
"griddle-react-bootstrap": "^0.4.2",
"griddle-react": "^0.3.1",
"react": "^0.14.6",
"react-bootstrap": "^0.28.2",
"react-dom": "^0.14.6",
"underscore": "^1.8.3"
}
webpackのconfig
測定に使用した設定パターンについて説明。A~Cがありますが、Aを基本として各設定を行っていきます。Bのdevtool
設定はB1~B5と細かく分けて試してみました。
なお、webpackのconfigにインクリメンタルビルドに効果がありそうなcache設定がありますが、watchモードの場合はデフォルト有効になるとのことですが、念のためtrue
を明示的に設定しています。
A 基準となる設定
cache: true
devtool: '#source-map'
- 使用プラグイン
- watchモード時: なし
- プロダクションビルド時: 下記の通り
new webpack.optimize.OccurenceOrderPlugin(true),
new webpack.optimize.DedupePlugin(),
new webpack.optimize.UglifyJsPlugin({ output: { comments: false } })
B devtool
を変更してSourceMapの方式を変更
devtoolの説明やBUILD PERFORMANCEを見ると、SourceMapの方式によってビルドパフォーマンスに大きく影響があるとのことなのでこれを変えてみます。
B1 devtool: '#inline-source-map'
に変更
{
...
devtool: '#inline-source-map'
}
B2 devtool: '#eval-source-map'
に変更
{
...
devtool: '#eval-source-map'
}
B3 devtool: '#eval-cheap-module-source-map'
に変更
{
...
devtool: '#eval-cheap-module-source-map'
}
B4 devtool: '#eval-cheap-source-map'
に変更
{
...
devtool: '#eval-cheap-source-map'
}
B5 devtool: '#eval'
に変更
{
...
devtool: '#eval'
}
C: externals
で外部ライブラリを対象外に
webpackの場合、configにexternals
を設定することで、ビルド時にnode_modules
以下から読んでいた外部ライブラリをバンドル対象外にできます。ビルド時にパースしなくなるので大きなパフォーマンス向上が望めます。
通常、アプリケーション以外のサードパーティ製のライブラリには変更が入らないことが多いので、できるならexternals
を使って分離しておくと良さそうです(ライブラリ側が用意してくれている必要あり)。
また、サードパーティ製のライブラリはCDNから読み込むようにする場合もこの設定になります。
今回の測定では、使用しているライブラリのうち下記の通り5つを設定して測定しました。
{
...
externals: {
"react": "React",
"react-dom": "ReactDOM",
"react-bootstrap": "ReactBootstrap",
"griddle-react": "Griddle",
"underscore": "_"
},
...
}
なお、実際に動かす場合はHTMLなどでこれらのライブラリを別途手動で読ませる必要があるので注意。また、ローカル環境だと下記のようにnode_modules
から読むことはできますが、実際のサーバにデプロイする際はちょっと工夫が必要になります。これについてはまた別の記事を書きたいと思います。
<script type="text/javascript" src="../node_modules/react/dist/react-with-addons.min.js"></script>
<script type="text/javascript" src="../node_modules/react-dom/dist/react-dom.min.js"></script>
<script type="text/javascript" src="../node_modules/react-bootstrap/dist/react-bootstrap.min.js"></script>
<script type="text/javascript" src="../node_modules/underscore/underscore-min.js"></script>
<script type="text/javascript" src="../node_modules/griddle-react/build/griddle.js"></script>
測定ケース
- watchモードでビルド (
webpack -w
) - watchモードでビルド後のインクリメンタルビルド (1ファイル更新)
- プロダクションビルド
のケースを測定。インクリメンタルビルドは2回目以降は速くなる傾向があるので複数回実行させました。
なお、誤差が結構(数百ms)ありブレるので、何回か試して真ん中くらいの値を雑に採用しています。まぁあくまで目安ということで。
結果
単位はmsです。
測定ケース | A | A+B1 | A+B2 | A+B3 | A+B4 | A+B5 | A+C | A+B1+C |
---|---|---|---|---|---|---|---|---|
watchモードでビルド | 4667 | 4634 | 4256 | 3722 | 3640 | 3567 | 1947 | 1930 |
インクリメンタルビルド(1回目) | 2438 | 2293 | 2012 | 1499 | 1633 | 1211 | 937 | 902 |
インクリメンタルビルド(2回目以降) | 1863 | 1461 | 880 | 903 | 863 | 875 | 707 | 689 |
プロダクションビルド | 17992 | 16046 | 6206 | 5699 | 5647 | 4981 | 2347 | 2318 |
考察
- ビルド速度は開発時しか基本気にしないのであれば、Reactのような大きなライブラリも含めて丸ごとビルドしてもインクリメンタルビルドであればそこそこ速い。
- SourceMapと
externals
設定の効果が大きい。特に、大きなライブラリを利用するケースだとかなり効いてくる。 - ただしSourceMapはデバッグ時にないと基本困るので、
devtool
設定を変えれば速くはなるが精度が落ちないレベルの物を使用した方が良いと思う。特にReactでJSXを使う場合は、eval-cheap-source-map
やeval
だとJSに変換された物にマップされてしまいJSXでデバッグできないので注意。また、性能とは別件なのですが、Chromeでeval
系だとブレークポイントが効かないという問題に遭遇しているため、個人的にはinline-source-map
を使っています。 - たくさん外部ライブラリを使う場合は
externals
を使って対象外にすることを検討したほうが良さそう。
補足
今回はBabelは使っていませんが、使用する場合はbabel-loaderのcacheDirectory設定を設定すると良さそうです。