Core-Web-VitalsのCLS改善をやっていく

## イントロ
どうも、AIに代替されつつあるヒト、`massu159`です。
AIとは喧嘩せずに仲良くやっていきたいですね。

今回はCLSの改善というテーマでやっていきます。
経緯としては、Next.jsを使ったJAMstack構成のWebサイトを作成していて、SEOに有利になるCore Web Vitalsを改善に取り組んでいました。
[PageSpeed Insights](https://pagespeed.web.dev/)と`Lighthouse`を使ってパフォーマンス診断を行ったところCLSのスコアが低く、これを重点的に改善したいと思い、備忘録もかねてまとめながら、CLS改善をやっていきます。
![Webサイトのスコア](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/619375/a1dea0e3-bfe3-9625-4dda-6a310642baa8.png)
現状はこんな状況です。

### Core Web Vitals
[web.dev](https://web.dev/learn-core-web-vitals/)によると、
Web 上での優れたユーザーエクスペリエンスの提供に不可欠な品質シグナルに関する統一されたガイダンスを提供するためのイニシアチブです。
### CLS
同じく[web.dev](https://web.dev/cls/)によると、
CLS は、ページの表示中に発生した予期しないレイアウトシフトごとにレイアウト シフト スコアの最大バーストを測定します。

概要はほどほどにして、早速作業始めていきます。
(Core Web Vitalsについて色々ググってみましたが、しかし)
私はAIとは仲良くやっていきたいので、`chatGPT`に改善策を提案してもらいましょう。

> CLSを改善するためには、以下の具体的な方法を試すことが効果的です。
> 1. 明示的なサイズ指定: 要素（画像や動画、広告など）に対して、幅や高さの明示的なサイズ指定を行います。これにより、コンテンツが読み込まれる前にその領域が確保され、レイアウト変化が発生しにくくなります。
> 2. 遅延読み込みの最小化: 遅延読み込みや非同期読み込みを使用する場合は、レイアウトの安定性を確保するために、表示領域を確保し、必要な要素が読み込まれる前にプレースホルダーやスケルトン画像を使用します。
> 3. 動的な要素の予約スペース: JavaScriptを使用して動的に要素を追加する場合は、要素の予約スペースを作成し、レイアウトの変化を最小限に抑えることが重要です。要素が追加される前に予約スペースを作成し、要素のサイズや位置が変更されるときにもスペースを確保します。
> 4. フォントの読み込みの最適化: フォントの読み込みに時間がかかる場合は、フォントをプリロードすることでCLSを改善できます。また、フォントのサイズが変更される際には、フォントのサイズを予め決定しておくことで、レイアウトの変化を最小限にします。
> 5. インラインスタイルの最適化: JavaScriptを使用してインラインスタイルを変更する場合は、変更前と変更後のスタイルを比較し、変更によるレイアウト変化を最小限にします。可能な場合は、クラスを切り替えるなどの方法を検討します。
これらの方法を組み合わせてCLSを改善することができます。Web開発者向けのツールやガイドラインを使用して、CLSの問題を特定し、解決策を見つけることをおすすめします。

ありがとう。。時短になりました。
コードのリファクタリングもAIにやってもらえばいいという思いが浮かんできましたが、備忘録も兼ねてますので自分で手を動かしていきたいと思います。そもそも最初の段階で、Core Web Vitalsを考慮した質のいいコードが書けていれば、この作業は不要なので、自戒の意味も込めて。。。

それでは、上記の提案に従って、改善してみましょう。
### 1.　明示的なサイズ指定

> 1.　明示的なサイズ指定: 要素（画像や動画、広告など）に対して、幅や高さの明示的なサイズ指定を行います。これにより、コンテンツが読み込まれる前にその領域が確保され、レイアウト変化が発生しにくくなります。

![描画の遷移](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/619375/a5c09811-1bfb-90f8-da16-093e93318e2b.png)
今回の私が作成したWebサイトの場合、ヘッダーに画像やアイコンが使われており、特にハンバーガーメニューが描画時、一瞬大きく表示されてその後、小さくなっているので、ちゃんと`width`と`height`を設定してあげます。ちなみにCSSはTailwindCSSを使って書いてます。
↓
ヘッダーに`height`を追加したので、解決！

と思ったのですが、実際は解決しておらず別の方法で解決しました。
各種アイコンは[FontAwesome](https://fontawesome.com/)を使用していて、`FontAwesome`はデフォルトではCSSをランタイムで読み込むので、ハンバーガーメニューを描画してから、CSSを適用しているので一瞬大きく表示されているようでした。
ですので、事前にCSSを適用し読み込んでおくことで解決しました。

```Header.tsx
  //faBarsはハンバーガーメニュー
  import {faBars} from '@fortawesome/free-solid-svg-icons'
+ import { config, library } from '@fortawesome/fontawesome-svg-core'
+ config.autoAddCss = false
+ library.add(faBars)
+ import '@fortawesome/fontawesome-svg-core/styles.css'
  ~~~
  以下省略
```
FontAwesomeの使い方については解説しません。
上記を解説すると、`config.autoAddCss = false`でランタイムでCSSを読み込む挙動を無効化します。
その後、`import '@fortawesome/fontawesome-svg-core/styles.css'`を追加して、事前にCSSを適用させるようにしました。
これで解決！



### 2.　遅延読み込みの最小化
> 2.　遅延読み込みの最小化: 遅延読み込みや非同期読み込みを使用する場合は、レイアウトの安定性を確保するために、表示領域を確保し、必要な要素が読み込まれる前にプレースホルダーやスケルトン画像を使用します。

遅延読み込みを行うことで必要な要素は先に読み込み、優先度が低い要素は遅れて読み込むのでWebページの表示速度を向上させます。しかし、遅延読み込みで逆にレイアウトの崩れにつながるということですね。
今回は、無視。

### 3.　動的な要素の予約スペース 
> 3.　動的な要素の予約スペース: JavaScriptを使用して動的に要素を追加する場合は、要素の予約スペースを作成し、レイアウトの変化を最小限に抑えることが重要です。要素が追加される前に予約スペースを作成し、要素のサイズや位置が変更されるときにもスペースを確保します。

こちらもあるあるですね。しかし今回は、`Next.js`を使っているとはいえ、全てのページをSSGで構築しています。build時にすべて静的ファイルにしているので、この対応も必要がなさそうです。


### 4.　フォントの読み込みの最適化
> 4.　フォントの読み込みの最適化: フォントの読み込みに時間がかかる場合は、フォントをプリロードすることでCLSを改善できます。また、フォントのサイズが変更される際には、フォントのサイズを予め決定しておくことで、レイアウトの変化を最小限にします。

私は、GoogleフォントをCSS側で`@import`していましたので、`HEAD`タグ内で`preload`しておきます。
```
<HEAD>
    <link 
        rel="preload"
        href="..."
        as="font"
    />
</HEAD>
```
### 5.　インラインスタイルの最適化
> 5.　インラインスタイルの最適化: JavaScriptを使用してインラインスタイルを変更する場合は、変更前と変更後のスタイルを比較し、変更によるレイアウト変化を最小限にします。可能な場合は、クラスを切り替えるなどの方法を検討します。

インラインスタイルの最適化も今回は改善するところがなさそうでしたので無視ですね。

### 結果
以上、改善後のスコアを見てみましょう。
![CLS修正後のスコア](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/619375/cfee571b-425f-02e2-cb66-a280a7705f2b.png)

`Cumulative Layout Shift（CLS）`が`0.002`まで下がりました。
`0`にはなりませんでしたが、これは今後の課題としましょう。
ちなみに、上記の結果はデスクトップサイズのものですが、携帯電話サイズでは`CLS`は`0`になりました。

## まとめ
CLSの改善というテーマでやってきましたが、私の場合CSSの改善という問題に置き換えられました。
TailwindCSSがよしなにやってくれておんぶに抱っこだったので、今回は学びになりました。