webサービスのパフォーマンスを改善したい話

こんにちはブリューアスwebフロントのsuginokoです。

今関わっているプロジェクトで
本当に読み込み遅いなーって(前から思ってはいたんです)思ってて、bundlejs分割して読み込み少しでも早くならないかな～ってなったのがきっかけ。
webpackでbundle分割をして読み込みをしたいとか、ファイルサイズを軽くしたいとか軽い気持ちは持っていたんです。
こんなこと知ってるよ、常識だっていう人いるかもしれないですが備忘録として書いていきます。


## こういう人向け
webpackはなんとなくでも理解できる人
ウチのサイト重いなーって思っている人
React+webpack使っている人
font重すぎる～で悩んでる人

## 目的
webサービスのビルド、読み込み速度を改善する
パフォーマンスを改善したい
※開発環境で試して、お客様には提案ベースとしてもっていきたいです。

## 現在のもろもろの情報
```
webpack 4.29.6
webpack-cli 3.3.0
react 16.8.6
```
自分の環境ではreact使ってのSPAの実装
まあまあな大きさのプロジェクト、更新頻度も高め
現状はentryしているものをビルドするとbundle.jsを自動生成しているだけ

## ビルドや読み込みの問題点
### JS側の問題点
【ビルド時】
今回は開発環境で試します
開発環境なのでhotreloadが入っており、minifyもしてなければ、consoleも削除してない状態です。

```
bundle.js 11.8MiB
```
ビルド時間78.47ｓ

※本番環境ではminifyしており、consoleも削除済みのため、開発よりは断然軽い

```
bundle.js 3.39MiB
```
単位がわからない方[こちら](https://academy.gmocloud.com/keywords/20170510/4314#:~:text=%E3%82%B5%E3%82%A4%E3%82%BA%E3%81%AF%E3%80%81KiB%EF%BC%88%E3%82%AD%E3%83%93%E3%83%BB,%E4%B8%878%2C576B%E3%80%8D%E3%81%A8%E3%81%AA%E3%82%8A%E3%81%BE%E3%81%99%E3%80%82)から

bundle.jsもbundle.cssも【big】とか言われちゃってます。大きいです。

【読み込み時】
スーパーリロードして確認

```
bundle.js 
size 12.6MB time2.73s
```
他の読み込み考えるとコイツだけで約3秒、、遅いですね。
こちらも開発環境だとconsole結構だしているんでなんと1分くらいかかる場合もあります。
※本番環境はsize3.2MBです

ここからとりあえずjs部分をなんとかもう少し軽くしたい。

### CSS側の問題点(というよりフォント)
また、css側でも一番はフォントが激重なのでなんとかしたいと思ってます。デザインの要望に応えるため、`HiraKakuProN` をfont-faceで埋め込んでいます。(Macにあるけど、windowsには存在しないため、埋め込みの必要があった)
こいつの読み込みサイズと速度を確認すると
(カスタムフォント入れるとこれだけ重いのだということをデザイナーさんには知ってほしい。。。)

```
HiraKakuProN-W3.woff　5.8MB　3.46ｓ
HiraKakuProN-W6.woff　5.7MB　3.46ｓ
```
一番長い読み込み時間となってます。こちらをなんとかしたい

### 全体の問題点
JSの問題、フォントの問題により、読み込み完了までの時間が
全ての読み込みが終了するまで約20秒。ユーザー(開発環境なので開発者)からするとかなり不快。
通常リロードでも16秒。
本番環境ですと5秒くらいなのですが、それでも不快。改善したいところ。

## 改善方法を模索する
### 不必要なモジュールの削除
言うまでもない(開発初期で使っていたけど、もう使ってないいらないものが残ってた)
使わないモジュールは削除します。

### splitChunksを使ってbundle.jsの分割
webpack4のsplitChunksを使います。
splitChunksはoptimizationプロパティにつけれられるもので、
複数のエントリーポイントで共有のモジュールを使っている場合、その共通モジュールだけ分離して(chunkに分離)設定するものです。
また、共通ファイルは共通なのでキャッシュからも読み込んでくれます。

このプロジェクトではReactを使っていますが、いたるところで呼び出していたjsファイルでReactがバンドルされていた、つまり重複してたところを共通化してくれるというわけです。
webpackのoptimizationオプションに追記するだけ。
凄く簡単。
オプションも沢山あります。
とりあえずお客様に提案ベースなのでinitialで対応して確認する。

```javascript
// webpack.config.js

module.exports = {
  //...
  optimization: {
    splitChunks: {
      name: 'vendor',
      // include all types of chunks
      chunks: 'initial'
    }
  }
}
```
共通モジュールをvendor.jsとして分割してビルドされます。


###  初期読み込みに必要ないものは遅延読み込みさせる
webpackで呼ばれるJSのほかに、cdnから読み込んでいるJSもindex.htmlに記載されています。
これらのscriptタグにdefer属性を入れます。
現在はデフォルト設定になっているため、HTMLパースの読み込みを阻害します。その解消のために追加します。
deferと似た属性としてasyncがありますが、deferと違うのは読み込み順序があるかないかの違いです。
deferは読み込み順序があり、順番によみますが、acyncは順序不定です。順番に読んでほしかったのでdeferにしました。


### フォントのサブセット化
フォントのサブセット化は更新頻度の高いサービスには不向きです。デザインの均一化が可能ではありますが、入れるだけで読み込みが激重になります。
[【2020年版】今さら聞けない、Webフォントの使い方！完全ガイド【まとめ】 – 東京のホームページ制作 / WEB制作会社 BRISK](https://b-risk.jp/blog/2020/06/webfont/)
webフォントを使っているわけではありませんが、こちら参考になります。
今回プロジェクトでは本来であればサイトに使われている文字だけ抽出してサブセット化したいところですが、運営上都合が悪いので明らかに日常生活では利用しないであろう漢字、文字を取り除いて軽量化をしていきます。(そのため、実験のみで、お客様には提案ベースで書いていくつもり)

どうサブセット化するかですが、
特に使用頻度の低い漢字を除外します。
何をもって使用頻度が低いかですが、
[JIS第１水準の漢字一覧](https://kanji.jitenon.jp/cat/jisdai1.html)
こちらを基準としてます。
一般的に日本人が使う漢字が第一水準の漢字で2,3,4とだんだん使われなくなる漢字になっているようです。

なので、第一水準のみの漢字をいれるパターン、第一第二水準の漢字を入れるパターンでサブセット化を行いました。(もちろん、漢字の他ひらがな、カタカナ、記号を含みます)


## 結果
#### javascript編
開発環境で再びビルドと読み込みをしました。
【ビルド時】

```
bundle.js 3.7MiB
vendor.js 8.13MiB
```
ビルド時間77.89ｓ
※ビルド時間はそこまで変わらず

【読み込み時】
スーパーリロードして確認

```
bundle.js
size 4.1MB time 826ms

vendor.js
size 8.5MB time 1.51s
```
でもずっと真っ黒画面からローディングまでが長かったところ、すぐページ表示は(体感的には)された気がする。
スーパーリロードで10秒
通常リロードで6秒で全読み込み完了するようになりました。

#### CSS(フォントのサブセット化) 編
第一水準のみ漢字をいれた

```
HiraKakuProN-W3.woff　0.9MB
HiraKakuProN-W6.woff　1.1MB
```

第二水準まで漢字を入れた

```
HiraKakuProN-W3.woff　2.0MB
HiraKakuProN-W6.woff　2.2MB
```
どちらも元々5.5MBあったので半分以下の軽さになりました。
これだけ重いことがわかっていながらリリース当初に組み込まなかったことは反省。

## おわりに
お客様には提案段階ですが、結構読み込み速度も早くなりそうなので早くいれたいです。
jsとcss記事分けてもよかったかも。
今気づきましたが会社のフロントの先輩が書いた管理画面では既にパフォーマンスバリバリ改善webpack書いてあったんで、先を越されてました。


## おまけ
ビルドの話だけでいえば、esbuildってのがビルド時間速くなるって記事をみました。
[ABEMAにesbuildを導入してWebのバンドル処理を69倍高速化した話 \| CyberAgent Developers Blog](https://developers.cyberagent.co.jp/blog/archives/26336/)
問題点は多々あるようですが、改善されたら使えそうですし、css in jsであればwebpackよりもこっちがいいのでは？とすらも思ったり。今回の改善策ではビルドは速くならなかったので実験してみたいですね。
人によっては180倍違うと言っている人もいたので、ビルド環境を考えたいのであればありかもしれない。