LCPの改善方法について

2021/03/19に公開

※本記事はweb.devを学習用にまとめたものです。解釈に齟齬等あればコメントください。

Overview

Googleで提唱されているWebページのパフォーマンス指標である LCP (Large Contentfull Paint) を最適化する具体的な手法について記載する。

LCPとは何かについては、Web Vitalsとは何かを参照してください。

LCPが悪化する4つの原因

1. サーバーレスポンスタイムが遅い

サーバーからのレスポンスが遅くなれば、当然画面へのレンダリング処理も遅くなる。

遅くなってるかどうかはTime to First Byte (TTFB)を計測すべし。

もしTTFBが遅いコンテンツに対して、レスポンスタイムを改善すればLCPも最適化できる。

2. JavaScriptやCSSによるレンダリングブロック

<link><script>のタグがあると、HTMLのパースが一時停止する。

HTMLのパースが完了しないと、レンダリングが始まらない。

従って、JSとCSSの読み込み方次第で、レンダリングの速度も改善する。

3. 動画像読み込みが遅い

<img>, <svg>, <video>, url()などが様々な動画像を読み込めば、当然描画時間に影響を与える。

たとえば、Above the fold(スクロールしないで見える領域)に画像コンテンツとかが多ければ、それだけLCPは劣化する。

4. クライアント側でのレンダリング処理

React, Vue, AngularなどでSPAを構築するときは注意が必要。

なぜなら、JavaScriptのダウンロードと実行が完了しないと、クライアント側には何も描画されないから。

どうすれば改善する??

計測して原因が特定できたら、あとは改善あるのみ

共通して言えることは、

  • コンテンツのサイズを縮小する
  • できるだけキャッシュさせる
  • 初期表示に不要なコンテンツは遅延読み込み
  • 初期表示に必要なコンテンツは事前読み込み

1. サーバーレスポンスタイムの改善

サーバー側の最適化

サーバ側がリクエストを受けてから、重たいロジックを処理したり、重たいSQLを流したりするとレスポンスは悪化する。

基本的にはフレームワークを使って動的なコンテンツを返しているはずなので、フレームワークごとのパフォーマンスガイダンスに従うがよし。

CDN&キャッシュの利用

ユーザから物理的に近い場所からレスポンスを返す、できるだけキャッシュで返す。

様々なレイヤでのキャッシュが利用できるため、検討すべし。

Service Workerの利用

バックグラウンドでService Workerを動かすことで、細かなキャッシュ制御ができる。

統計的にもService Workerが有効なサイトはLCPが小さいため、導入を検討すべし。

外部リソース取得時の事前接続

レンダリングに重要なコンテンツを外部から取得するときも、LCPに影響を与える。

preconnectやdns-prefetchを使うことで、ブラウザはあらかじめコネクションを確立することが可能。

<head><link rel="preconnect" href="https://example.com" />
  <link rel="dns-prefetch" href="https://example.com" />
</head>

2. JavaScriptやCSSの読み込みを最適化

CSS/JSのMinify

空白, インデント, コンマなど不要な文字を取り除く

NON-criticalなCSS/JSの遅延読み込み

初期表示で必要なもの不要なものに分割し、不要なものは遅延読み込みをさせる。

<link rel="preload" href="stylesheet.css" as="style" onload="this.rel='stylesheet'">

criticalなスタイルのインライン化

above-the-foldに必要なスタイルは<head>内に直接埋め込む

使用しないpolyfillsの最小化

対応するブラウザの種類やバージョンを決めて、babelなどを利用して最適化する

Serve modern code to modern browsers for faster page loads

3. リソースの読み込みを最適化

画像サイズの圧縮と最適化

不必要なものを除去、圧縮、フォーマット変換(WebP)を検討すべし。

重要な画像のプリロード

CSSやJSから新たにコンテンツを取得し、それがレンダリングに必要なものであればプリロードさせる。

例えば、HEROの動画像やフォントファイルなどが該当する。

<link rel="preload" as="script" href="script.js" />
<link rel="preload" as="style" href="style.css" />
<link rel="preload" as="image" href="img.png" />
<link rel="preload" as="video" href="vid.webm" type="video/webm" />
<link rel="preload" href="font.woff2" as="font" type="font/woff2" crossorigin />

テキストファイルの圧縮

gzipやBrotliなどのアルゴリズムにより、サーバ側で圧縮して配信するように設定する。

ユーザ環境に適応したリソース選択

ユーザのネットワーク速度などの環境に応じて、配信するコンテンツの種類やサイズを選択させる。

使用可能なユーザ環境の例)

  • navigator.connection.effectiveType: 接続回線の種類
  • navigator.connection.saveData: データセーバー設定の有無
  • navigator.hardwareConcurrency: CPUコア
  • navigator.deviceMemory: メモリ

Service Workerによるキャッシュ

Service Workerでキャッシュ制御をさせると、静的コンテンツを何度も取得させる必要がなくなる。

特に、回線速度が遅い端末に対しては効果的。

4. レンダリング処理の最適化

JavaScriptの最小化

JavaScriptやCSSの読み込みを最適化での議論と同じ

SSR(Server Side Rendering)の利用

サーバ側でHTMLをレンダリングさせることで、SPAの欠点とされている

「JavaScriptがすべて読み込まれないと何も表示されない」

という問題が解決する。

一方で、レスポンスタイムが悪化したり、TTI(操作するまでの時間)が悪化する恐れもある。

SSG(Static Site Generator)の利用

SSRの欠点である「レスポンスタイムの悪化」を補うために、事前に静的コンテンツをビルドしておくという手法。

しかしながら、TTIの悪化は解決できない。

感想

大事なことは「上に書いてあること全部やる」じゃなくて

「自分のサイトでのボトルネックを測定して対処する」、な気がする。

全部やると結構大変そうだし、トレードオフも発生しそう。

Discussion