webpackによるビルドを高速化するための設定がいくつかあるのでそれらの効果を(雑にですが)測ってみました。
なお、ビルド時間の測定には手前味噌ですが、React.js用のGridコンポーネントであるGriddleをBootstrapデザインにするgriddle-react-bootstrapexamplesアプリを使用しています(動いている物はこちらから見れます)。使用ライブラリは下記のpackage.jsonの通り。TypeScriptで書いているので、webpack + ts-loaderでビルドしています。Babelは使用していませんが傾向はたぶん同じになるかと思います。

package.json
  "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-mapevalだとJSに変換された物にマップされてしまいJSXでデバッグできないので注意。また、性能とは別件なのですが、Chromeでeval系だとブレークポイントが効かないという問題に遭遇しているため、個人的にはinline-source-mapを使っています。
  • たくさん外部ライブラリを使う場合はexternalsを使って対象外にすることを検討したほうが良さそう。

補足

今回はBabelは使っていませんが、使用する場合はbabel-loaderのcacheDirectory設定を設定すると良さそうです。