ヤフー株式会社は、2023年10月1日にLINEヤフー株式会社になりました。LINEヤフー株式会社の新しいブログはこちらです。LINEヤフー Tech Blog

テクノロジー

Core Web Vitalsの良好URL数を99%以上に維持してきた方法

本記事は2022年11月に開催した「Tech-Verse 2022」で発表したセッションを要約したものです。アーカイブ動画を文末に掲載しています。質疑応答の様子も収録されていますのでぜひご覧ください。

WebサイトにとってSEOは重要です。SEOのスコアが低いと検索順位が下がり、閲覧数が減ってしまいます。それに伴って売り上げが減ったり、登録者数が減ったりすることもあります。

また、UXも非常に重要です。ユーザーがページを訪問したとき、表示が遅い、見づらい、ボタンが押しづらいなどの課題があると、次第にサイトが利用されなくなってしまいます。

今回紹介するCore Web Vitalsは、SEOやUXに影響するものです。Webサイトの運用者にとっては非常に重要なもので、簡単な改善方法が多くあります。今回は、ヤフーが運営する就職・転職サポートサイト「Yahoo!しごとカタログ」で実践し特に効果が高かった改善方法をご紹介します。

Core Web Vitalsについて

Core Web Vitalsとは、Webページをローディングしたときの3つのUX指標です。2021 年6月から、SEOランキングに影響を与える要素の1つとなりました。以下、指標の3つを順に説明します。

LCP(Largest Contentful Paint)

ページ読み込みを開始してから、ビューポートに最大コンテンツが表示されるまでの時間を指します。2.5秒以内に表示されれば「良好」と認識されます。Core Web Vitalsの3つの指標のうち、最もSEOに影響を与えると言われています。

下図で具体的なイメージを紹介します。左から右の順にコンテンツが表示されます。

コンテンツの表示順のイメージ図。最大コンテンツが表示されるまでの秒数が2.5秒以内ならLCPは良好

緑色で囲まれた部分が最大コンテンツです。最初は左上のテキスト、途中で上部のテキスト、最後は画像が最大コンテンツと認識され、緑色で囲まれた画像が表示されるまでの時間がLCPです。

CLS(Cumulative Layout Shift)

ビューポート内の視覚要素がどれくらい移動しているかを測定する指標です。移動量が多いと判定されるとスコアは低くなります。アコーディオンやエクスパンドなど、ユーザーが意図している移動に関しては、0.5秒以内に完了していれば問題ありません。

FID(First Input Delay)

ユーザーの操作から、ブラウザがイベントハンドラーの処理を開始するまでの時間です。「操作」とは、リンクやボタンのクリックなどフォームを操作することで、スクロールやズームは含まれません。操作から処理開始までの時間が100ミリ秒以内であれば良好と判定されます。

Webサイトのパフォーマンスは、Google Search Consoleの「ユーザーエクスペリエンス」というレポートで把握できます。下図はYahoo!しごとカタログの2021年1月から4月までのパフォーマンスです。2月は良好なURLが約30%でしたが、4月には良好なURLが98%になりました。現在は、モバイルでは99%以上を、デスクトップでは100%のパフォーマンスを維持できています。

Yahoo!しごとカタログの2021年1月から4月までの「ユーザーエクスペリエンス」レポート

どのように改善したか

実際にやってみて効果が高かった事例を紹介します。

LCP改善のポイント

最大コンテンツをいかに早く表示するかがポイントです。まずは、何が最大コンテンツなのかの把握が必要です。Google Chromeのデベロッパーツール内にある「Lighthouse」を使って特定できます。実行するとレポートが作成され、診断結果の「Largest Contentful Paint element」に最大コンテンツの場所が書いてあります。

実際の改善方法について説明します。ブラウザはHTMLを上部から読み込みます。しかし途中にスクリプトタグがあると、JSファイルのダウンロード、解析、実行されるので、LCP要素を読み込むまで時間がかかってしまいます。それを防ぐためには、scriptタグにdeferやasync属性をつけると良いでしょう。また、最大コンテンツより下部にscriptを移動させることで、LCP要素を高速に読み込ませる方法もあります。

最大コンテンツが画像の場合、サイトに表示したい画像が複数あると、どれが最初にダウンロードされるかは分かりません。そのような場合はpreloadを活用すれば、ほかの画像よりも先にダウンロードして表示させられます。その他の手段としては、画像フォーマットをWebPにするとファイルサイズが2分の1から6分の1程度になるので、ダウンロードスピードが速くなります。WebPをサポートしていないブラウザを考慮する場合は、pictureタグやNext.jsのImageで対応すると良いでしょう。

クライアントサイドだけでなく、サーバーサイドの改善もLCPに有効です。下図は、Yahoo!しごとカタログのシステム構成を簡単に表したもので、サーバーサイドは、BFF、API、データベースから構成されています。

Yahoo!しごとカタログのシステム構成図

サーバーサイドのLCPの改善ポイントは、レスポンス時間の短縮です。Yahoo!しごとカタログでは、BFFは400ミリ秒以内にレスポンスするよう保っています。具体的には、BFFとAPIの処理を直列から並列に変更し、Node.jsにおいては、Promise.all()を利用したり、すべてのPromiseを実行後にawaitします。

並列処理ができない場合はIntersection Observerを使って、ページロード時ではなく表示位置の近くになったらデータを取得します。これにより、ページロード時の表示が早くなります。

ここまで工夫しても遅い処理がある場合は、Redisなどのメモリキャッシュサーバーを利用して一度受け取ったレスポンスをキャッシュに格納し、2回目以降のレスポンスを高速化します。下図ではAPIに対してRedisを使っていますが、状況に応じてBFFでもRedisを利用します。

メモリキャッシュサーバーを利用して2回目以降のレスポンスを高速化している構成のイメージ図

データベースでSQLの実行が遅い場合はテーブルにインデックスを張ったり、SUMやAVERAGEなどの集計クエクエリをなるべく実行しないように、あらかじめバッチでデータを加工しています。APIやデータベースの改善はフロントエンドエンジニアだけでは難しいこともあるので、バックエンドエンジニアにも協力してもらっています。

CLSの改善ポイント

画像、遅延取得したデータ表示、広告といった要素がなるべく動かないようにします。

画像を動かないようにする方法からご紹介します。画像の幅と高さが決まっている場合は、widthとheightを設定します。幅によって高さが変わる場合は、高さを確保するためのdivを用意します。例えば、画像の縦横比が1:5の場合は、Padding-topを20%に指定するといった具合です。imageタグについては、position: absolute;とwidthを100%にすることで、高さを変えることなく表示できます。サービスが対応しているブラウザによっては、aspect-ratioを利用した方がシンプルに実装できる場合もあります。

遅延取得のデータを動かないようにする場合は、高さが大きく変わることを念頭に置かなければなりません。データによっては、高さを正確に判断できない場合もあります。ただ、こうしたケースではスケルトンを用意して高さを確保しておけば、レイアウトシフトの影響はほぼ受けません。例えば、広告といった高さが不明なデータ取り扱う場合、あらかじめmin-heightを設定しておくと、CLSへの影響は小さくなります。

FIDの改善ポイント

FIDの改善の前に、なぜInput Delayが発生してしまうのか説明します。下図は一般的なWebページのブラウザの読み込みの流れを表したものです。

一般的なWebページのブラウザの読み込みの流れを表した図

灰色は、ページのリクエストやCSS、JS、Webフォント、画像のリクエストです。黄色は、レスポンスを受け取った後のブラウザの処理。処理内容としては、HTML、CSS、JavaScriptの解析、ブラウザへの描画、JavaScriptの実行などが含まれます。黄色の状態のときはブラウザがビジー状態となり、入力を受け付けても応答できません。

ブラウザがビジーになりやすい原因としては、主にJSのファイルサイズが大きいことが考えられます。Webpackを利用している場合、JSのファイルサイズは「Webpack Bundle Analyzer」で分析できます。実行すると、以下のようなレポートが生成されます。

Webpack Bundle Analyzerで生成されたレポート。JSのファイルサイズが大きいが位置や色などで可視化されている

左上からファイルサイズが大きい順に並んでいます。レポートの一番赤い部分をクリックすると、詳細が表示されます。今回の場合はmoment、Chart.js、lodash.jsが原因だということが分かりました。 原因を特定した後の流れを説明します。

まずはmoment。Yahoo!しごとカタログでは利用されている箇所が少なかったので、素のJavaScriptへ変更しました。利用箇所が多い、また、多言語対応が必要な場合では、Day.jsなど、ファイルサイズが小さいnpmパッケージに置き換えるという方法もあります。

次にlodash.js。非常に便利なライブラリですが、すべてインポートするとファイルサイズが大きくなります。関数ごとにエクスポートされていることに着目して、今回はインポート方法を変更しました。

最後にChart.js。グラフを描画するnpmパッケージです。今回はファーストビューでグラフを表示する必要はなかったので、表示が必要なタイミングでChart.jsをダウンロードできるダイナミックインポートを利用しました。具体的にはIntersection Observerを使い、グラフに近づいたタイミングでダイナミックインポートが動くという流れです。

これら3つの対応で、ページロード時のJSファイルサイズを70%削減できました。

Webpackを使っていない場合は、Lighthouseの診断結果を参考にします。以下の図の青い枠で囲った中に、大きな負荷を与えているJSのURLが表示されているので、それを参考にして不要なJSがないか確認することが可能です。

Lighthouseの診断結果

よくあるシチュエーションとしては、以前までは使っていたが現在は不要となったJSを読み込み続けてしまっていたり、タグマネージャーを活用していてその機能が不要なページにまでJSを適用してしまっていたりといったことが挙げられます。

また他に具体例を挙げると、GA4のアクセス解析では、利用する機能によってJSファイルサイズが変わります。動画を扱っていないのに拡張計測機能の動画エンゲージメントを利用すると、FIDに影響を与えてしまうので設定を確認した方が良いです。

どのように高品質を維持したか

品質を改善・維持する場合は、最初に現状を把握する必要があります。Google Search Consoleを利用すれば問題を特定できます。問題点を示すレポートが表示されるので、対応すべきかどうか判断できます。

ただ課題もあります。Google Search Consoleで表示されたデータは、「Chrome UX レポート」を使ってユーザーのGoogle Chromeブラウザから取得したデータです。つまり、すでにUXに影響が出てしまっている事後的な検知になってしまいます。

事前に検知する方法にはLighthouse CIがあります。Lighthouse CIは自動でLighthouseを実行できるツールで、既存のCIのパイプラインに組み込むことが可能です。以下の図は、CIに組み込んでいる例を示したものです。

Lighthouse CIをパイプラインに組み込んだ構成の例図

大まかな流れを説明します。

まず開発者が実装してコードをコミットすると、CircleCIなどのCI/CDツールでLighthouseを実行します。実行結果のレポートがHTMLで作成され、LighthouseサーバーにアップロードしてからMySQLに保存します。その後はLighthouse CIの実行結果のスコアを検証し、設定したしきい値を下回った場合はエラーとして通知。開発者は、Lighthouseサーバーにあるレポートを確認して対応します。

Lighthouse CIのレポートは、Gitのコミットと実行時のレポートとひも付けることが可能です。クリックしてレポートの詳細を見れば、どのコミットが悪影響を及ぼしているのか把握できます。Diffでレポート同士の比較もできます。Lighthouse CIを使えば原因特定の時間を短縮でき、事前に修正することが可能です。

Webサイトを長期間にわたって運用していると、人的リソースの問題が発生します。知識がある人が異動したり、新しく加わったばかりで勝手が分からなかったりすることもあるでしょう。その他には、時間的リソースの課題もあります。例えば、大規模な新機能開発があり、Core Web Vitalsへの対応が難しくなるケースもあるかもしれません。

解決方法は、対応方針を明確にし、対応方法を簡潔にするだけです。 実際にチームで行っていることをご紹介します。

1つ目の具体例としては、技術スタックとして、React、Next.js、 express(BFF)を使っているWebサービスでは、以下の対応方針を定めました。

  • 画像表示には必ずNext.jsのImageを利用する
  • BFFでは並列処理を行う
  • 処理が遅いデータは遅延取得し、スケルトン表示する
  • 重いJSや条件によって使わないコンポーネントは、ダイナミックインポートを使う
  • ファイルサイズの大きいJSを使う場合は、チーム内で相談してから進める

2つ目の具体例は、技術スタックにCMSであるMovable Typeを利用し、HTMLとCSSで実装しているケースです。この場合では以下の対応方針を定めました。

  • preloadとWebPを使う
  • 画像の高さを事前確保する
  • imgタグには、loading=”lazy”を使う

loading属性はCore Web Vitalsを意識するというよりも表示パフォーマンスに影響するので方針に入れています。

Yahoo!しごとカタログでは、こうした取り組みによってCore Web Vitalsを改善してきました。参考にしていただけたら幸いです。

アーカイブ動画

こちらの記事のご感想を聞かせください。

  • 学びがある
  • わかりやすい
  • 新しい視点

ご感想ありがとうございました


松浦 慶多
エンジニア
広告サービス、グループ会社を経て、2019年よりYahoo!しごとカタログのフロントエンドをリードする。

このページの先頭へ