webpackを可視化するツールを紹介

フロントエンドのビルド（webpack）に関わる部分でパフォーマンス改善を考える時に、
現状を可視化するため、いくつかプラグインを使ってみましたので、軽く紹介したいと思います

可視化したい対象は以下の2つです
・webpackの速度改善の為、遅いloaderやpluginを知りたい
・バンドルファイルのサイズを減らしたいので、バンドルファイル内の各パッケージがどのくらいの容量を占めているか知りたい

今回分析に使ったコードはこちらです
https://github.com/kurosame/vuejs-boilerplate

## 遅いloaderやpluginを知りたい
### `speed-measure-webpack-plugin`
```sh
$ yarn add --dev speed-measure-webpack-plugin
```

```js:webpack.config.js
const SpeedMeasurePlugin = require('speed-measure-webpack-plugin')
const smp = new SpeedMeasurePlugin()

// webpackConfigをラップする
module.exports = smp.wrap({
  entry: {
  ...
})
```

```sh
$ npx webpack
```

■ 結果
<img width="254" alt=" 2018-04-17 18.54.38.png" src="https://qiita-image-store.s3.amazonaws.com/0/168197/a7771675-0f7d-dc11-8b44-d5dee2511046.png">

全体で約6.5秒くらいかかってるのですが、そのうちのほとんどがTypeScript関連の処理に時間がかかっているのが分かります
（babel-loader and ts-loader and tslint-loaderの所）

実際のwebpack設定を見ると以下のようになってました

```js:webpack.config.js
...
module.exports = {
  module: {
    rules: [
      ...
      {
        test: /\.ts$/,
        use: [
          {
            loader: 'babel-loader?cacheDirectory'
          },
          {
            loader: 'ts-loader',
            options: {
              appendTsSuffixTo: [/\.vue$/],
              transpileOnly: true
            }
          },
          {
            loader: 'tslint-loader',
            options: {
              typeCheck: true
            }
          }
        ],
        exclude: /node_modules/
      }
      ...
    ]
  }
}
```

ここをターゲットに調べるとtslint-loaderのtypeCheckに3.5秒かかっていることが分かりました
少し調べるとtslint-loaderのtypeCheckが遅いことがissuesに上がってました
https://github.com/wbuchwalter/tslint-loader/issues/76

## バンドルファイル内の各パッケージがどのくらいの容量を占めているか知りたい
### `webpack-bundle-analyzer`
```sh
$ yarn add --dev webpack-bundle-analyzer
```

```js:webpack.config.js
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin

module.exports = {
  ...
  plugins: [
    new BundleAnalyzerPlugin()
  ]
}
```

```sh
$ npx webpack
```

ブラウザが`http://127.0.0.1:8888/`で立ち上がり、以下のような結果が表示されます

<img width="1666" alt=" 2018-04-17 17.52.47.png" src="https://qiita-image-store.s3.amazonaws.com/0/168197/e5266bd3-5ef2-411c-e559-f12da93b34e9.png">

今回分析に使ったコードでは、webpack実行時に2つのファイルを出力するようにしています
実装したコードをバンドルした`build-[ハッシュ値].js`とサードパーティー系のパッケージのみをバンドルした`vendor-[ハッシュ値].js`になります
vendorの方がサイズが大きくなると予想してましたが、結果はほぼ同じくらいのサイズでした

結果を見てみると、babel-runtimeとnormalize.cssらへんが大きいことが分かります

babel-runtimeは新しい書き方（EcmaScript）で実装したコードをレガシーブラウザでも動くように変換するライブラリなので、
IEなどをターゲットにしているプロジェクトだと使わないわけにはいきません

normalize.cssはクロスブラウザでデフォルトのスタイルの差異を無くすため、初期デフォルトスタイルを提供してくれるライブラリです
今まで意識してなかったので、ここまで重いとは思いませんでした。。
調べてみると、今回分析に使ったコードでは`normalize.css`はv7.0.0を使っています。
現在の最新はv8.0.0でこのバージョンでは古いバージョンのブラウザを対象外としている為、大幅な軽量化がされたみたいです
単純にバージョンを上げるだけでもバンドルサイズの削減ができそうです

また、IEをサポートする必要がない場合は、modern-normalizeというライブラリを検討するのも良さそうというもの分かりました
https://github.com/sindresorhus/modern-normalize

またnormalize.cssを使わずに自作する選択肢もありかもしれません

### `webpack-bundle-size-analyzer`
webpack-bundle-analyzerだとvendorが上手く可視化できなかったので、webpack-bundle-size-analyzerを使って可視化しました

```sh
$ npx webpack --config webpack.vendor.config.js --profile --json > stats.json
$ npx webpack-bundle-size-analyzer stats.json
```

<img width="185" alt=" 2018-04-18 14.14.41.png" src="https://qiita-image-store.s3.amazonaws.com/0/168197/f5b0a613-20c6-d7c8-2fca-caf74519481e.png">

### `source-map-explorer`
こちらもwebpack-bundle-analyzerと同様にバンドルファイルの中身を可視化できます

```js:webpack.config.js
module.exports = {
  output: {
    filename: '[name]-[hash].js',
    path: path.resolve(__dirname, 'dist'),
    sourceMapFilename: '[file].map',
  },
  devtool: '#source-map' // inline-source-mapだと駄目だった
}
```

```sh
$ npx source-map-explorer dist/bundle-[ハッシュ値].js
```

<img width="1676" alt=" 2018-04-18 11.47.26.png" src="https://qiita-image-store.s3.amazonaws.com/0/168197/d51b2967-e37e-6a10-1dc1-4794bfc58c06.png">

こちらは全体のサイズやパーセントが見れるし、グリッドデザインで見やすいです

### `webpack-visualizer-plugin`
こちらもwebpack-bundle-analyzerと同様にバンドルファイルの中身を可視化できます

```sh
$ yarn add --dev webpack-visualizer-plugin
```

```js:webpack.config.js
const Visualizer = require('webpack-visualizer-plugin')

module.exports = {
  ...
  plugins: [
    new Visualizer()
  ]
}
```

先程のプラグインと比べて、だいぶ見せ方が違いますね

<img width="755" alt=" 2018-04-18 14.28.17.png" src="https://qiita-image-store.s3.amazonaws.com/0/168197/f08d1826-ffa3-bda0-317a-0fe037e9c5d4.png">

カーソルを合わすと、以下のようにサイズとパーセントが表示されます

<img width="688" alt=" 2018-04-18 14.29.10.png" src="https://qiita-image-store.s3.amazonaws.com/0/168197/68dd8696-649d-5aa1-7323-863ed91fa361.png">