React Bundle.js の最小化


はじめに

本編は、webpackTypeScript を使用する前提になってます。React に限らず、他のSPAにも適用できます。

なぜ最小化が必要

複数のライブラリ使うと、ビルド後の bundle.min.js も膨大(10mb↑)になって、ユーザの初期ロード時間が長くなります。ネットワーク弱者は、表示できない可能性もあります。

どんな方法があるの

  • 必要だけの importにしましょう
  • Chunks しましょう
  • Compession しましょう
  • Content-type: gzip にしましょう

具体的にどうすれの?

これから色んなツールを駆使し、サイズを減りましょう

まずは分析

bundle.js の内部構造を分析し、大きいサイズのライブラリから、減らして行きます。
最初に登場するのは、webpack-bundle-analyzer です。

bundle.min.js の内部構造を図面化してくれるツールです。大体こんな感じ

webpack

import { BundleAnalyzerPlugin } from 'webpack-bundle-analyzer';

const configs: Configuration = {
  plugins: [
    new BundleAnalyzerPlugin()
  ]
};

export default configs;

次は、stats.jsonです。
バンドルされたライブラリの内部、jsonファイル使った場合、圧縮されませんため、stats.jsonから分析します。下記コマンドから作成できます。

stats.json
webpack --config webpack.prod.ts --profile --json > stats.json

次はライブラリ構造の調査

先の分析結果を元に、割合が大きいのライブラリから調査する、今回は lodash.js を対象にします。node_modules にライブラリのソース確認できますので、ちょっとのぞきましょう。

lodash
   | - isNull.js
   | - map.js
   | - math.js
   | - min.js

上記構造から、このライブラリは複数ファイルに分散されることが分かりました。必要の分だけimportします。

改善方法
- import * as _ from 'lodash';
+ import map from 'lodash/map';

stats.json の調査

webpack-bundle-analyzer結果に、大きいなjsonブロックがなければ、調査しなくても大丈夫です。典型的のは、momentです。下記リンクからlatest.jsonを検索すれば、詳しく書いてます。

webpackのbundle後のJavaScriptのサイズを減らしている話

Chunks を有効にする

Code Splitting 手法の1つ、その他の色々ありますので、リンク先を見てください。有効する方法は簡単なので、下記設定を参考してください。
https://webpack.js.org/guides/code-splitting/

webpack
import { BundleAnalyzerPlugin } from 'webpack-bundle-analyzer';

const configs: Configuration = {
  optimization: {
    splitChunks: {
      name: true,
      cacheGroups: {
        commons: {
          chunks: 'initial',
          minChunks: 2
        },
        vendors: {
          test: /[\\/]node_modules[\\/]/,
          chunks: 'all',
          priority: -10
        }
      }
    },
    runtimeChunk: true
  },
};

export default configs;

圧縮機能を有効にする

compression-webpack-pluginの登場です。ビルド同時に、圧縮されたファイルも出力される。
node-zopfliを使えば、もっと圧縮率を上げられます。

webpack
import CompressionPlugin from 'compression-webpack-plugin';
import zopfli from 'node-zopfli';

const configs: Configuration = {
  plugins: [
    new CompressionPlugin({
      test: /\.js$/,
      filename: '[path].gz[query]',
      algorithm: (source, compressionOptions, callback) => {
        return zopfli.gzip(Buffer.from(source), compressionOptions, callback);
      }
    })
  ]
};

export default configs;

最後は、Content-type 設定しましょう

圧縮されるファイルは、そのまま使えませんので、サーバ上、gzip配信機能の有効が必要です。

まとめ

dynamic importloadable componentも、サイズ最小化の有効手段なので、是非使ってみてください。