【Core Web Vitals】Webパフォーマンスを改善するなら、まずは重要な指標について理解しよう

## パフォーマンス改善について連載します！

QiitaにてWebパフォーマンスの改善に取り組み始めました！
そこで、パフォーマンス改善をするためにどんな観点で分析をして、どのように改善をしようとしているかを記事にまとめてみようと思います。

ただ、パフォーマンス改善と言っても、関連する領域が広く、とても1記事にまとめられないので、複数記事に分けて連載していくことにしました。
今想定しているのは以下ですが、分量によっては記事がさらに分割されてしまうかもしれません。

### シリーズ内容 (予定)

1. :rocket: Core Web Vitals/指標について 【←これ】
2. :rocket: [Lighthouse CI + Datadogを使ってパフォーマンスを継続的に計測する - Qiita](https://qiita.com/kyntk/items/2086ece17387dccea61c)
3. :rocket: 【番外編】[開発者ツールのLighthouseを使いこなせてますか？ - Qiita](https://qiita.com/kyntk/items/80a88f0894657a815cf8)
4. :rocket: [LCPの改善でLighthouseだけ見てない？効果的に改善していくためのステップ](https://qiita.com/kyntk/items/1f69a80476294713082b)
5. FIDを改善する
6. CLSを改善する
7. キャッシュについて

もし興味のある方は、この記事をストックしていただくか、[Twitter（@kyntk_1128）](https://twitter.com/kyntk_1128)をフォローしていただければ記事の更新ごとに通知しますので、ぜひよろしくお願いします！

## この記事で説明すること

さて、パフォーマンスの改善をする際に、手当たり次第に策を打っても無駄が発生してしまいます。
たまたま改善することもあれば、全く効果が無いものがあったりと、成果につながるかどうかは運になってしまいます。

そのため、パフォーマンス改善においてまず大事なことは、**指標を決めて計測し続けること**です。

Qiitaでは、Core Web Vitalsやその他の指標を継続的に監視しながら改善しようとしています。

当記事では、このCore Web Vitalsやその他の指標について理解をすることを目的に説明していきます。
そして各種指標について理解した上で、どこを改善していくべきなのかということについても話します。

:::note
以降は https://web.dev/vitals/ などを参考にまとめています
:::

## パフォーマンスの指標について

まずはじめに、「パフォーマンスが高い」と聞いてどんなものをイメージしますか？

- ページが表示されるまでに待つ時間が短い
- 次々と追加コンテンツが読み込まれる
- クリックやスクロールに対してサクサク・スムーズに反応する
- ローディングで長い時間待たされることがない

など、イメージするものは様々だと思います。

そしてサイトのパフォーマンスに対しての感じ方はユーザーによってばらついたり、メトリクス上の数値が同じでも感じ方が変わったりすることがあります。具体的には、

- デバイスのスペックやネットワークの状況によって感じ方が変わる
- ページの読み込み方法によって、全体の読み込み時間は変わらなくても体感として早く感じることがある
- ページの表示はできても応答が遅いことがある

といったものが挙げられます。

そのため、指標を決めても実はユーザーの体感と一致していなかったりすることがあります。
また、過去、計測ツールが乱立して、それぞれ取得できるものが違うことで、評価指標が複雑化していたこともあります。

そんな中、ユーザーの体感値に合い、かつシンプルな指標として、Googleによって2020年にWeb Vitalsが発表されました。

## Core Web Vitals

Web Vitalsの中でも最も重要な指標とされているものです。

先程感じ方が人それぞれと言いましたが、Web VitalsはWeb上でのユーザー体験にフォーカスをしています。

現時点では「LCP」、「FID」、「CLS」の3つの観点がCore Web Vitalsとして発表されています。
しかしこの3つで完結ではなく、今後も改善、追加していく予定とのことです。

それぞれについて詳しく説明していきます。

### 【LCP】 読み込みのパフォーマンス

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/143317/931155c4-0484-e590-8abe-0da7fff622bd.png)

:::note
Largest Contentful Paint (LCP) 指標は、ビューポート内に表示される最も大きい画像またはテキストブロックのレンダリング時間を、ページの初期読み込み開始タイミングと比較してレポートします
:::

https://web.dev/lcp/

LCPはページの読み込みのパフォーマンスの指標ですが、実は他にも読み込みに関する指標はありました。(`First Paint`、`First Meaningful Paint`など)

しかし、ユーザー体験の観点から読み込みパフォーマンスを表すのは「最も大きい要素がレンダリングされるタイミング」が適していると分かったため、LCPが定められています。

#### 読み込みに関するその他の指標

実は他にも、Core Web Vitalsには含まれないですが、Web Vitalsに読み込みに関する指標があります。

First Contentful Paint(視覚コンテンツの初期表示時間、FCP) や Time to First Byte (サーバーの初期応答時間、TTFB)です。
LCPに関連して読み込みのパフォーマンスを把握するためにこれらの指標は役に立ちます。

https://web.dev/fcp/

https://web.dev/ttfb/

#### LCPの対象となる最大要素

先程、LCPの説明で、「最も大きい画像またはテキストブロック」とありましたが、もう少し具体的に定められています。

- `<img>`要素
- `<svg>`要素内の`<image>`要素
- `<video>`要素
- `url()`関数を介して読み込まれた背景画像が含まれている要素
- テキストノードやその他のインラインレベルのテキスト要素の子要素を含むブロックレベル要素

他にに要素が追加される可能性もあるようです。

[web.dev](https://web.dev/lcp/)を読むと、より詳細に要素の決定条件が記載されていますが、自分のサイトにおける最大要素はDev toolのPerformanceタブで確認することができるので確認してみましょう。

#### LCPの計測方法の具体例

web.devに乗っている例がとてもわかりやすいので引用します。

以下の画像の緑の枠の部分がその時点でLCPと判定されている要素です。
読み込みとともに最大要素の判定が変わっていくのが分かります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/143317/d7633e26-68f2-1682-6ff1-e5c775e1ee44.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/143317/f915ce02-c78f-6220-5fc8-e3f0435ef8f9.png)

ただし、レンダリング途中でユーザーのインタラクション(タップ、スクロール、クリック)があった場合新しいレポートは停止されます。(表示される要素が変わる可能性があるため)

#### ブラウザ上で画面の描画(Paint)の流れを把握する

ここまでで最大要素について知ることができたと思います。
次はLCPをどう改善するかを考えていくのですが、そのためにはどのようにブラウザが描画をしているかを知る必要があります。

効果的に改善するには、ネットワーク処理、スクリプティング処理、レンダリング処理、ペインティング処理などそれぞれ詳しく知るべきなのですが、まずはざっくり流れを把握しようと思います。

最もシンプルな例では、最大要素の描画までの流れは以下のようになっています。

```mermaid
flowchart
  リクエスト -.->|request| HTML
  HTML --> DOM
  HTML -.->|request| CSS
  CSS --> CSSOM
  DOM  --> LCP(表示)
  CSSOM --> LCP
  style LCP fill:#f9f
```

HTMLをパースしてDOMを構築し、スタイルシートを取得してCSSOMを構築します。
そこからDOMとCSSOMをもとにレンダーツリーを構築し、画面に描画します。
画面に最大要素が描画されると、そこがLCPとして計測されます。

ただ、近年はJavaScriptを使用したサイトが多くなっており、LCPまでがこんなにシンプルなことは稀だと思います。
以下はJavaScriptと、JavaScriptから読み込まれる画像が描画された結果、最終的に画像がLCPとなる例を示しています。

```mermaid
flowchart
  リクエスト -.->|request| HTML
  HTML --> DOM
  HTML -..->|request| CSS
  HTML -...->|request| JavaScript
  CSS --> CSSOM
  DOM  ----> 表示
  CSSOM --> 表示
  JavaScript --> DOM2
  JavaScript -.->|request| 画像
  表示 ---> 表示2
  DOM2 --> 表示2
  表示2 --> LCP(表示3)
  画像 --> LCP
  style LCP fill:#f9f
```

このように、1つ目の例と、2つ目の例で、LCPを改善するといっても関わってくる部分が変わることが分かります。
ブラウザがどういう順序で画面の描画を行っているのか、その上で自分のサイトはどのような特徴があるのかを知っておきましょう。

ここからLCPのボトルネックを探していく方法についてはまた別記事で説明していきます。

ブラウザのレンダリングまでについてもっと詳しく知りたい方はこちらなどをご覧ください

https://www.html5rocks.com/en/tutorials/internals/howbrowserswork/

https://docs.google.com/presentation/d/1boPxbgNrTU0ddsc144rcXayGA_WF53k96imRH8Mp34Y/mobilepresent?slide=id.ga884fe665f_64_6

### 【FID】 インタラクティブ性

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/143317/725c4473-6032-e6bb-2dcf-9707572f6f7b.png)

:::note
First Input Delay(FID)は、ユーザーが最初にページを操作したときから、その操作に応じてブラウザーが実際にイベントハンドラーの処理を開始するまでの時間
:::

https://web.dev/fid/

FIDは表示されたページを操作しようとしてから実際に応答されるまでの時間を指標とします。
この指標が悪いサイトは、全然反応してくれない、使っててイライラするサイトになってしまいます。

FIDの注意点としては、計測対象はイベント処理の遅延までで、それが実際にUIに更新されるまでの時間は計測されていません。

#### インタラクティブ性が悪化する要因

入力などの応答は、基本的にブラウザのメインスレッドが処理をします。
しかしアプリケーションが、読み込んでいるJavaScriptを解析していたり、その他の処理を実行してメインスレッドがビジー状態になっていたりすると応答できないことがあります。

また、サーバーサイドでHTMLがレンダリングされているサイトだと、一見画面が完成しているようで、実はイベントリスナーの設定が終わっておらず、クリックをしてもイベントハンドラが発火しないということも起こりえます。

#### なぜ初回のインタラクションなのか

First Input Delayとあるように、FIDは最初のインタラクションを指標に置いています。なぜなら

- 初回入力の遅延は、サイトの第一印象となるため
- インタラクティブ性の問題はページの読み込み時に発生することが多いため
- 初回のインタラクション問題の原因と、それ以降のアクションへの原因は一致しないことが多く、切り分けたいため

ページ表示直後、メインスレッドの占有を解消することが、インタラクティブ性の体感向上につながるようです。

#### FIDは、正直難しい

個人的に、Core Web Vitalsの中で、FIDが最も難しいなと感じています。
なぜなら、ユーザーがいつアクションを起こすかによって、全く結果が変わるためです。

たとえば、Qiitaの記事ページは、記事を読むことが主目的でページ表示直後にボタンをクリックしたり、何かを入力することはなく、基本スクロールしかしません。
FIDではスクロールは評価の対象外のため、FIDに該当するようなイベントが訪問直後には行われず、メインスレッドがアイドル状態になって初めてユーザーアクションが行われる可能性が高そうだと想定されます。

そのため、Core Web Vitalsだけを指標として置いていて、FIDの数値が良かったとしても実はメインスレッドが忙しくて、他の箇所に悪影響を及ぼしていたり、一部のユーザーにだけ大きな影響起きていたりといった可能性もありえます。

「たまたまメインスレッドがビジーなタイミング」でユーザーがアクションをしたらFIDが悪化するし、逆にアイドル状態であればFIDがとても良くなります。
このように、ユーザー操作に依存しているので、改善サイクルを回すのが難しいなと感じています。

#### Total Blocking Time と Time to Interactive

インタラクティブ性については、「Total Blocking Time (合計ブロック時間、TBT)」 と 「Time to Interactive (操作可能になるまでの時間、TTI)」という指標がWeb Vitalsに含まれており、FIDに影響を及ぼします。

これらはラボ環境といわれる環境で計測できる指標で、Lighthouseなどによって計測することができます。

先程、FIDはユーザー操作に依存していると述べましたが、TBT、TTIはユーザー操作に依存せず、メインスレッドがどれだけ忙しいかを計測することができます。
これによって、実際のFIDは知ることはできないですが、FIDが最悪の場合どのくらい悪化する可能性があるのかを想定することができます。
そのため、この指標を改善すれば、FIDが悪化する可能性を下げることができます。

https://web.dev/tbt/

https://web.dev/tti/

### 【CLS】 視覚的な安定性

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/143317/e08cd318-3de5-a2be-8fae-165672314366.png)

:::note
Cumulative Layout Shift (CLS)は、ページの表示中に発生した予期しないレイアウトシフトごとにレイアウトシフトスコアの最大バーストを測定します
:::

https://web.dev/cls/

CLSとは何か？を知るために一番わかりやすいのが、上記ページに乗っている12秒の動画だと思います。

サイト上のボタンを押そうとした瞬間に、その上部に別のコンテンツが表示され、ボタンがずれることにより誤操作してしまう経験は誰にでも一度はあると思います。

2020年にこの指標が発表された当初は、LCP、FIDと比べると、個人的には突然現れた感がありましたが、最近はライブラリ側でCLSを防ぐ実装なども増えてきて、対策自体は最もシンプルにできるものだと考えています。

#### レイアウトシフトスコア (画面に占める割合 × 移動した割合)

CLSでは、ビューポート内における要素位置が変更される割合を測定しており、レイアウトシフトスコアと呼んでいます。

これは、「ビューポートのうちどの程度の割合の要素が影響を受けたか」(画像の赤い点線の領域) × 「どの程度の距離を移動したか」(画像の矢印の占める割合)の掛け合わせで算出されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/143317/40426f75-2f93-b096-80d6-38d040e3f419.png)
(web.devから引用)

そしてこのレイアウトシフトスコアが、セッションウィンドウと呼ばれる期間において最大どの程度になったのかを指標とします。

実際はもう少し複雑な計算をしているので、こちらをご覧ください (2021年6月にそれまでと指標の算出方法が変更されています)

https://web.dev/evolving-cls/

#### ユーザーの操作によって起きるレイアウトシフトは除外される

すべてのレイアウトシフトが問題になるわけではありません。
ユーザーが操作をした時にレイアウトシフトが起きることはよくあります。

そこで、ユーザー操作から**500ms**以内に発生するレイアウトシフトは計算から除外されます。

そのため、例えばユーザー操作を起因として、データを取得し、画面に描画するまで500msを超えてしまうものなどもあるかもしれませんが、その場合はユーザー操作直後にインジケーターなどを表示し、レイアウトシフトが起きないようにスペースを確保しておくことで回避できます。

その他にも、レイアウトシフトが想定される場合には事前にその領域を確保しておくほうが良いでしょう。

## Core Web Vitalsに含まれないWeb Vitals

これまでに何度か出てきましたが、Core Web Vitals以外にもWeb Vitalsは存在します。

- [Time to First Byte (TTFB)](https://web.dev/ttfb/)
- [First Contentful Paint (FCP)](https://web.dev/fcp/)
- [Time to Interactive (TTI)](https://web.dev/tti/)
- [Total Blocking Time (TBT)](https://web.dev/tbt/)

また、現在はWeb Vitalsとして指標に定められていないものとして、「実行時の応答性」や「滑らかさ」などがあり、これらは今後Web Vitalsに追加されていく可能性もあるため、引き続き注視していく必要があります。

## サイトごとに違う部分もある

ここまでは、多くのサイトに共通する指標ですが、実際に自分たちのサイトのパフォーマンスを改善するとなった時に、これらでは網羅できない部分もあると思います。
例えばQiitaでイメージすると、

- 記事のプレビューの速度
- 記事の投稿の速度
- ストックをするまでの応答速度
- LGTMを押した後の反応とマイクロインタラクション
- 広告の表示速度
- もっと見るボタンを押したときの追加読み込み速度
- などなど

といったように、Qiitaならではの重要な指標がたくさん出てきます。

このようにCore Web Vitalsでは網羅できない、個々のサイト特有のパフォーマンス指標も考慮する必要があります。

これらを計測するには[Custom metrics](https://web.dev/custom-metrics/)を設定します。

## どこを改善するのかを決める

さて、ここまででたくさんの指標が出てきましたが、実際にパフォーマンスを改善するとなったら、どの指標をどの程度改善するかを決めていく必要があります。

すでにお分かりかと思いますが、パフォーマンスと言っても様々で、目に入ったものを適当に改善したのでは、達成したいゴールを達成できなくなる可能性がとても高いです。

そのため、パフォーマンス改善に当てることのできる期間、コストを考慮してどこに注力するのかを決めることが大切です。

目指す指標が決まれば、それを定期的にモニタリングをしながら、改善を繰り返し、指標の変化を確認することでパフォーマンスの向上につなげることができます。
このモニタリングについては次回のテーマとしてまとめる予定です。

## まとめ

Webパフォーマンスを改善する第1歩として大事な指標についての説明をしました。
これらをきちんと理解することが、今後の分析のために必須条件となりますので、理解を深めましょう。

次回はこれらの指標を継続的に監視し続けるための方法について説明していきます。

このシリーズはまだまだ続く予定ですので、LGTMとストック、[Twitter（@kyntk_1128）](https://twitter.com/kyntk_1128)での続報をお待ち下さい!

## 参考

- web.dev
  - [Web Vitals](https://web.dev/learn-web-vitals/)
  - [User-centric performance metrics](https://web.dev/user-centric-performance-metrics/)
  - [Largest Contentful Paint (LCP)](https://web.dev/lcp/)
  - [First Input Delay (FID)](https://web.dev/fid/)
  - [Cumulative Layout Shift (CLS)](https://web.dev/cls/)
  - [Evolving the CLS metric](https://web.dev/evolving-cls/)
  - [The business impact of Core Web Vitals](https://web.dev/vitals-business-impact/)
  - [Custom metrics](https://web.dev/custom-metrics/)
  - [Defining the Core Web Vitals metrics thresholds](https://web.dev/defining-core-web-vitals-thresholds/)
- [Chromium Blog: The Science Behind Web Vitals](https://blog.chromium.org/2020/05/the-science-behind-web-vitals.html)
- [Chrome Speed - Web Vitals Changelogs](https://chromium.googlesource.com/chromium/src/+/master/docs/speed/metrics_changelog/README.md)
- [How Browsers Work: Behind the scenes of modern web browsers - HTML5 Rocks](https://www.html5rocks.com/en/tutorials/internals/howbrowserswork/)
- [Life of a Pixel - Google スライド](https://docs.google.com/presentation/d/1boPxbgNrTU0ddsc144rcXayGA_WF53k96imRH8Mp34Y/mobilepresent?slide=id.ga884fe665f_64_6)