KDE BLOG

Webデザインやコーディングについて書いています

【webpack速習】vol.13: Build Performance

下記ページのざっくりとした翻訳。

webpack.js.org

ビルド/コンパイルのパフォーマンスを向上させるために役立つヒント。

General

開発用、本番用の両方のビルドで役立つベストプラクティスの紹介

Stay Up to Date(最新に保つ)

最新の webpack、node.js、npm or yarn を使う(常にパフォーマンスの改善を図っているため)

Loaders

必要最小限の数のモジュールにローダーを適用する。
下記のように、include を使うとそのディレクトリ配下のみに適用されるので効率が良い。

▼ webpack.config.js

module.exports = {
  //...
  module: {
    rules: [
      {
        test: /\.js$/,
+        include: path.resolve(__dirname, 'src'),
        loader: 'babel-loader'
      }
    ]
  }
};

Bootstrap

追加のローダー/プラグインにはそれぞれ起動に時間がかかる。できるだけ少ないツールを使用するようにする。

Resolving

下記手順で解決速度を上げることができる

  • ファイルシステムの呼び出し数が増えるにつれて、resolve.modulesresolve.extensionsresolve.mainFilesresolve.descriptionFiles の項目数を最小限にする
  • シンボリックリンクを使用しない場合は、resolve.symlinks:false を設定する(例: npm link or yarn link
  • カスタム解決プラグインを使用する場合、resolve.cacheWithContext:false を設定する。これはコンテキスト固有ではない

Dlls

DllPlugin を使用して、あまり頻繁に変更されないコードを別のコンパイルに移動することでアプリケーションのコンパイル速度が向上する。
しかしビルドプロセスが複雑になる。

具体的な使い方などは下記記事が参考になりそう。
webpackのDLLバンドルを使ってビルドを速くする - Qiita

Smaller = Faster

コンパイルの合計サイズを小さくすると、ビルドパフォーマンスが向上する。
チャンクを小さくするようにする。

  • 小さなライブラリを使う
  • 複数ページのアプリケーションでは、splitChunksPluginを使う
    • async モードで使う
  • 不要なコードの削除
  • 現在開発しているコードの一部をコンパイルする

Worker Pool

現時点でよくわからないので省略

Persistent cache(永続キャッシュ)

cache-loader で永続的なキャッシュを有効にする。
package.jsonpostinstall コマンドを使って、キャッシュディレクトリを削除する。

postinstall コマンドとは npm install コマンドの終了時に実行されるコマンドのこと

Development

開発時に役立つヒントの紹介

Incremental Builds

  • WebPackの watch モードを使用する
    • 他のツールを使ってファイルを見たり、webpackを起動したりしない
      • 組み込みの watch モードはタイムスタンプを追跡し、キャッシュ無効化のためにこの情報をコンパイルに渡す
    • 設定によっては、watchはポーリングモードに戻る。
      watchされているファイルが多いと、これによってCPUの負荷が大きくなる。
      このような場合は、watchOptions.poll を使ってポーリング間隔を長くすることができる

Compile in Memory

下記のユーティリティは、ディスクに書き込むのではなく、メモリ内のアセットをコンパイルして提供することでパフォーマンスを向上させている

  • webpack-dev-server
  • webpack-hot-middleware
  • webpack-dev-middleware

stats.toJson speed

webpack 4はデフォルトでは stats.toJson() で大量のデータを出力する。
リビルドのステップで必要な場合を除き、statsオブジェクトの一部を取得しないこと。 v3.1.3以降のwebpack-dev-serverには、リビルドのステップごとにstatsオブジェクトから取得されるデータ量を最小限に抑えるための大幅なパフォーマンス修正が含まれている。

Devtool

異なるdevtool設定のパフォーマンスの違いに注意

  • eval は最高のパフォーマンスを発揮するがコード変換には役立たない
  • cheap-source-map は、多少悪いマッピングで大丈夫であれば、より高性能である
  • リビルドには eval-source-map の変種を使用する

ほとんどの場合、cheap-module-eval-source-map が最良の選択肢。

Avoid Production Specific Tooling(production 固有のツールを避ける)

特定のユーティリティ、プラグイン、およびローダーは、本番用にビルドするときにのみ意味がある。

たとえば、開発中に TerserPlugin を使用してコードを縮小したり壊したりしても意味がない。これらのツールは通常、開発から除外する必要がある。

  • TerserPlugin
  • ExtractTextPlugin
  • [hash]/[chunkhash]
  • AggressiveSplittingPlugin
  • AggressiveMergingPlugin
  • ModuleConcatenationPlugin

Minimal Entry Chunk

現時点でよく分からないので省略

Avoid Extra Optimization Steps(余分な最適化手順を避ける)

webpackはサイズと負荷パフォーマンスのために出力を最適化するために余分なアルゴリズムの働きをする。これらの最適化は、小規模のコードベースではパフォーマンスに優れているが、規模の大きいコードベースではコストがかかる可能性がある

▼ 最適化をしない設定

module.exports = {
  // ...
  optimization: {
    removeAvailableModules: false,
    removeEmptyChunks: false,
    splitChunks: false,
  }
};

Output Without Path Info

webpackには、出力バンドルにパス情報を生成する機能がある。しかし、これは何千ものモジュールを束ねる大きなプロジェクトにガベージコレクションのプレッシャーをかける。 options.output.pathinfo 設定でこれをオフにする。

module.exports = {
  // ...
  output: {
    pathinfo: false
  }
};

Node.js Version

Node.jsの ver 8.9.10 - 9.11.1 の間で、ES2015の MapSet の実装にパフォーマンス低下のリグレッションがあった。
リビルドの速度改善を望む場合、上記のバージョンを使用するのを避けた方が良い。

TypeScript Loader

ts-loader は内部の TypeScript watch mode APIs を使い始めた。これにより、各反復で再構築されるモジュールの数が劇的に減少した。
この experimentalWatchApi は、通常のTypeScript watch mode自体と同じロジックを共有しており、開発用には非常に安定している。さらに高速なインクリメンタルビルドを行うには、 transpileOnly をオンにする。

module.exports = {
  // ...
  test: /\.tsx?$/,
  use: [
    {
      loader: 'ts-loader',
      options: {
        transpileOnly: true,
        experimentalWatchApi: true,
      },
    },
  ],
};

注:ts-loaderのドキュメントでは cache-loader の使用を推奨しているが、これは実際にはディスクへの書き込みによるリビルドを遅くする。

再度タイプチェックを行うには ForkTsCheckerWebpackPlugin を使用する。
完全な例は下記より参照。
ts-loader/examples/fork-ts-checker-webpack-plugin at master · TypeStrong/ts-loader · GitHub

Production

本番用に役立つヒントの紹介

小さなパフォーマンスの向上のためにアプリケーションの品質を犠牲にしないこと。最適化品質は、ほとんどの場合、ビルドパフォーマンスよりも重要であることに留意すること。

Multiple Compilations

複数のコンパイルを使用する場合は、次のツールが役立つ

Source Maps

source map は重いので本当に必要でなかったら使わない


Specific Tooling Issues(特定のツールのイシュー)

下記ツールには、ビルドパフォーマンスを低下させる可能性のある特定の問題がある

Babel

TypeScript

  • 別のプロセスでタイプチェックするために fork-ts-checker-webpack-plugin を使用する
  • タイプチェックをスキップするようにローダーを設定する
  • happyPackMode:true / transpileOnly:truets-loader を使用する

Sass

node-sass に Node.jsスレッドプールからのスレッドをブロックするバグがある。使うときは thread-loaderworkerParallelJobs: 2 にセットして使う。