パフォーマンスチューニングを学び始めてみる

# 初めに
[HCB Advent Calendar 2021](https://qiita.com/advent-calendar/2021/hcb-2021)
アドベントカレンダー16日目の記事になります (大遅刻)。

テストコードやパフォーマンスチューニングができないWebフロントエンジニアの需要は少ないといわれたので、
必死こいて勉強する記事です。

ゆるく記事を書くので興味がある方は覗いてってください（メモ書きのような記事です）

勉強資料として
[Web Speed Hackathon 2021 mini](https://github.com/CyberAgentHack/web-speed-hackathon-2021)
を使います。

## 本題

なにもいじってない状態でのスコアです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553841/14e64bec-115b-710e-a9f8-72dfda6cb693.png)

### build
./client/package.json

```
"build": "cross-env NODE_ENV=development webpack",
```

↓

```
"build": "cross-env NODE_ENV=production webpack",
```

デベロップだと多数のチェックを行ってしまう。結果、アプリケーションサイズの肥大化、速度の低下がおきる。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553841/003f7d01-2911-c30b-a009-0c6ad8fc5b31.png)

## SourceMap
./client/webpack.config.js

```
devtool: 'inline-source-map'
```

↓

```
devtool: NODE_ENV === 'production' ? false : 'inline-source-map',
```

元の設定だと、作成したコードのトランスパイル後も人が解釈可能な形のまま (デバッグしやすい) の状態にして埋め込んでしまっている。
なので、圧縮した形に直す設定にしてあげる。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553841/30d5d74a-180a-4d6b-e92a-7145df929fc5.png)

結果を見ると`First Contentful Paint`と`Speed Index`、`Largest Contentful Paint`、`Time to Interactive`、`Total Blocking Time`が大幅に削減されました。
ここで、各項目の意味を記しておきたいと思います。

First Contentful Paint：ユーザーがページに移動した後、ブラウザーがDOMコンテンツの最初の部分をレンダリングするのにかかる時間をスコアで表示したもの。フォントの読み込みに大きく影響される。
スコアの簡易的な対応は次の表のようになる[参照：First Contentful Paint](https://web.dev/first-contentful-paint/)。

|時間（秒）|Lighthouseで表示される色|
|---|---|
|0～1.8|緑 (早い)|
|1.8～3|オレンジ (まあまあ)|
|3～|赤 (遅い)|

Speed Index：ページのうち、最初に画面上に見えるコンテンツがどれだけ早く描画されるかを測定する。
スコアの簡易的な対応は次の表のようになる ([参照：Speed Index](https://web.dev/speed-index/) )。

|時間|Lighthouseで表示される色|
|---|---|
|0～3.4|緑 (早い)|
|3.4～5.8|オレンジ (まあまあ)|
|5.8～|赤 (遅い)|

Largest Contentful Paint：ページ上で最初に画面上に見える要素の内、最大のコンテンツ要素が画面にレンダリングされるタイミングを測定する。

スコアの簡易的な対応は次の表のようになる ([参照：Largest Contentful Paint](https://web.dev/lighthouse-largest-contentful-paint/) )。

|時間（秒）|Lighthouseで表示される色|
|---|---|
|0～2.5|緑 (早い)|
|2.5～4|オレンジ (まあまあ)|
|4～|赤 (遅い)|

Time to Interactive：ページがユーザーの操作を受け付ける準備が終わるまでの時間。次の3つの条件を満たすまでの時間を指す。

- First ContentfulPaintによって測定された有用なコンテンツが表示される。
- ページにある各要素にイベントハンドラーがセットされる。
- 50ミリ秒以内にユーザーの操作に応答できる。

スコアの簡易的な対応は次の表のようになる ([参照：Time to Interactive](https://web.dev/interactive/) )。

|時間（秒）|Lighthouseで表示される色|
|---|---|
|0～3.8|緑 (早い)|
|3.8～7.3|オレンジ (まあまあ)|
|7.3～|赤 (遅い)|

Total Blocking Time：マウスのクリック、画面のタップ、キーボードの押下などのユーザー入力への応答がブロックされている合計時間を測定する。First Contentful PaintとTime to Interactiveの間にあるすべての長いタスクのブロック部分を加算して算出される。ここでいう長いタスクとは50ms以上実行タスクのことを指し、50ms以降の時間がブロック部分となる。Lighthouseが85ミリ秒の長いタスクを検出した場合、ブロック部分は35ミリ秒となる。

スコアの簡易的な対応は次の表のようになる ([参照：Total Blocking Time](https://web.dev/lighthouse-total-blocking-time/) )。

|時間（ミリ秒）|Lighthouseで表示される色|
|---|---|
|0～200|緑 (早い)|
|200～600|オレンジ (まあまあ)|
|600～|赤 (遅い)|

### Mode (Webpack)
./client/webpack.config.js

```
mode: 'none',
```

↓

```
mode: 'production',
```

この設定をすることで、コンパイル後のJavaScriptが縮小＆難読化されます。

こちらの記事に詳しく書いてあります（わかりやすいです）。[参照：webpackのmodeは重要！難読化からMinifyまでする優れモノ](https://www.konosumi.net/entry/2018/06/23/024057)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553841/6a25f01a-c33e-9b33-0c49-1c388d4144b3.png)

### @babel/preset-react (babel)
./client/babel.config.js

```
development: true,
```

↓

```
development: false,
```

開発に固有の動作が切り替わる(https://babeljs.io/docs/en/babel-preset-react))。
詳しいことは調べきれなかった。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553841/ed03c295-239c-061c-7c93-8248feb8ea7b.png)

### purgeの利用 (tailwind)
./client/tailwind.config.js 

```
module.exports = {
  darkMode: false,
};
```

↓

```
module.exports = {
  darkMode: false,
  purge: {
    enable: true,
    content: ['./index.html', './src/**/*.jsx']
  },
};
```

tailwindすべてをファイルに盛り込むとすごく重くなるので、いらないものをパージ (purge) する。

```
\\ 変更前
Assets:
  styles/main.css (4.27 MiB)
  scripts/main.js (1.14 MiB)
```

↓

```
\\ 変更後
Assets:
  styles/main.css (463 KiB)
  scripts/main.js (1.14 MiB)
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553841/99782568-91ac-9efd-bdb3-6421907cd4d3.png)

### cssnanoの利用
tailwindの公式がcssnano使えといっているのでcssnanoを使う。
まずはパッケージをインストールする。

```
cd client
yarn add cssnano --dev
```

./client/postcss.config.js 

```
\\ 変更前 (tailwindの設定のみの場合)
Assets:
  styles/main.css (463 KiB)
  scripts/main.js (1.14 MiB)
```

↓

```
\\ 変更後
Assets:
  styles/main.css (382 KiB)
  scripts/main.js (1.14 MiB)
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553841/256ed454-1e2e-dc13-5adc-ef8d8e5a5da0.png)

### Render-Blocking Resources 
./client/src/index.html

htmlを解析して DOM を構築する作業を JavaScript や CSS が妨げないようにする。
JavaScript の読み込みは async/defer を用いる。どちらも JavaScript のコードをDOMの描画度ともに並列で行う。async は読み込みが終了した段階で実行を DOM のレンダリングを止めて JavaScript のコードを実行する。defer は DOM の描画後に JavaScript を実行する。依存するライブラリなどを使う場合、defer を利用する。
css の読み込みは rel 属性に preload を指定する (この場合、as 属性に style を指定する)。しかしこのままだとcssが適応されないので、onload="this.onload=null;this.rel='stylesheet'"も追加する。
より詳しい参考になる記事 =>（[参考：レンダリングを妨げるJavaScriptとCSSを削除するためにやったこと](https://qiita.com/haruna-nagayoshi/items/71327d1e3e9fef187abf)）


```
\\ 変更前 (tailwindの設定のみの場合)
<script src="/scripts/main.js"></script>
<link rel="stylesheet" href="/styles/main.css" />
```

↓

```
\\ 変更後
<script src="/scripts/main.js" defer></script>
<link rel="preload" href="/styles/main.css" as="style" onload="this.onload=null;this.rel='stylesheet'"/>
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553841/80ccde7a-7b65-cca1-f57f-8215c0531d45.png)


# まとめ
パフォーマンスチューニングは奥深く、まだまだ書くべきことがあるので、10点の壁を越えた切りのいいここでいったんこの記事は締めたいと思います。

来週の記事もパフォーマンスチューニングについて書きたいと思います。

---
筆者：@takashinishikawapub
ブログ：https://creatorsfantasyfpn.com/tech_study/
github：https://github.com/takatakunishi
twitter：https://twitter.com/takashi54461358
wantedly：https://www.wantedly.com/id/takashi_nishikawa_24
一言：実務バイトしたいです (Webフロント)