初回入力遅延を最適化する

ユーザー操作に迅速に応答する方法。

クリックしても何も起こらないこのページを操作できないのはなぜですか?validate

First Contentful Paint(FCP)と Largest Contentful Paint(LCP)はどちらも、コンテンツがページ上に視覚的にレンダリング(ペイント)されるまでの時間を測定する指標です。ペイント時間は重要ですが、読み込みの応答性、つまりページがユーザー操作にどれだけ速く応答するかは捕捉されません。

First Input Delay(FID)は Core Web Vitals の指標で、サイトのインタラクティビティや応答性に関するユーザーの第一印象を捕捉します。ユーザーが最初にページを操作してから、ブラウザが実際にその操作に応答できるまでの時間を測定します。FID はフィールド指標であり、ラボ環境ではシミュレーションできません。レスポンス遅延を測定するには、実際のユーザー操作が必要です。

良い fid 値は 2.5 秒、悪い値は 4.0 秒を超え、その間はすべて改善が必要

ラボで FID を予測するために、Total Blocking Time(TBT)をおすすめします。これらは異なるものを測定しますが、TBT の改善は、通常は FID の改善に対応しています。

FID が低い主な原因は、大量の JavaScript の実行です。ウェブページでの JavaScript の解析、コンパイル、実行の方法を最適化すると、FID を直接削減できます。

JavaScript の実行が多い

メインスレッドで JavaScript を実行している場合、ブラウザはほとんどのユーザー入力に応答できません。つまり、メインスレッドがビジー状態の場合、ブラウザはユーザー操作に応答できません。これを改善する方法は次のとおりです。

長いタスクを分割する

1 つのページに読み込む JavaScript の量を減らそうとしている場合は、長時間実行されるコードをより小さな非同期タスクに分割することをおすすめします。

長時間のタスクは、ユーザーが UI が応答しなくなる可能性がある JavaScript の実行時間です。メインスレッドを 50 ミリ秒以上ブロックするコードはすべて、長時間タスクとみなされます。処理に時間がかかるタスクは、JavaScript の肥大化(ユーザーが必要とする以上の量の読み込みと実行)の可能性を示しています。長いタスクを分割することで、サイトでの入力遅延を短縮できます。

Chrome DevTools での時間のかかるタスク
Chrome DevTools のパフォーマンス パネルで時間のかかるタスクを可視化

コード分割や長いタスクの分割などのベスト プラクティスを採用すると、FID は著しく改善されるはずです。TBT はフィールド指標ではありませんが、最終的に操作可能になるまでの時間(TTI)と FID の両方の改善に向けて進行状況を確認するのに役立ちます。

インタラクションの準備のためにページを最適化する

JavaScript に大きく依存するウェブアプリで FID スコアと TBT スコアが低下する一般的な原因は、次のとおりです。

ファーストパーティのスクリプトが実行されると、インタラクションの準備が遅れる可能性がある

  • JavaScript のサイズが肥大化している、実行時間が長い、チャンクが非効率的な状態になっていると、ページがユーザー入力に応答するまでの時間が遅くなり、FID、TBT、TTI に影響を与える可能性があります。コードと機能を段階的に読み込むことで、この作業を分散し、インタラクションの準備を整えることができます。
  • サーバーサイドでレンダリングされたアプリは、画面上にすぐにピクセル ペイントされているように見えますが、大規模なスクリプトの実行(イベント リスナーを接続するためのリハイドレーションなど)によってユーザー操作がブロックされることに注意してください。ルートベースのコード分割が使用されている場合は、数百ミリ秒、場合によっては数秒かかることがあります。サーバー側でより多くのロジックをシフトするか、ビルド時により多くのコンテンツを静的に生成することを検討してください。

アプリのファーストパーティ スクリプトの読み込みを最適化する前と後の TBT スコアは次のとおりです。重要でないコンポーネントの負荷の高いスクリプトの読み込み(および実行)をクリティカル パスから移動させることで、ユーザーはページをより早く操作できるようになりました。

ファーストパーティのスクリプトの最適化後の、Lighthouse の TBT スコアの改善。

データの取得はインタラクションの準備の多くの側面に影響を与える可能性がある

  • カスケード取得のウォーターフォール(コンポーネントの JavaScript やデータ取得など)を待機すると、操作のレイテンシに影響する可能性があります。データ取得のカスケードへの依存を最小限に抑えることを目指します。
  • 大規模なインライン データストアは HTML 解析時間をプッシュし、ペイントとインタラクションの両方の指標に影響を与えます。クライアントサイドで後処理する必要のあるデータの量を最小限に抑えることを目指します。

サードパーティ スクリプトの実行もインタラクションのレイテンシを遅らせる可能性がある

  • 多くのサイトにはサードパーティのタグと分析が含まれているため、ネットワークがビジー状態で、メインスレッドが定期的に応答しなくなり、操作のレイテンシに影響する可能性があります。サードパーティのコードのオンデマンド読み込みを確認します(たとえば、スクロールしなければ見えない範囲の広告は、スクロールしなければ見えない位置にスクロールされるまで読み込まないでください)。
  • 場合によっては、サードパーティのスクリプトは、メインスレッドでの優先度と帯域幅の点でファーストパーティのスクリプトを先取りし、ページが操作可能な状態になるまでの遅れを生じさせることがあります。ユーザーに最大の価値をもたらすと思われるものを優先的に読み込むようにします。

ウェブ ワーカーを使用する

メインスレッドがブロックされることは、入力遅延の主な原因の 1 つです。ウェブワーカーを使用すると、バックグラウンド スレッドで JavaScript を実行できます。UI 以外のオペレーションを別のワーカー スレッドに移動すると、メインスレッドのブロック時間を短縮し、FID を改善できます。

サイトでウェブ ワーカーを簡単に使用できるように、次のライブラリの使用を検討してください。

  • Comlink: postMessage を抽象化して使いやすくするヘルパー ライブラリ
  • Workway: 汎用のウェブワーカー エクスポータ
  • Workerize: モジュールをウェブ ワーカーに移動する

JavaScript の実行時間を短縮する

ページ上の JavaScript の量を制限することで、ブラウザが JavaScript コードの実行に費やす時間を短縮できます。これにより、ブラウザがユーザー操作に応答し始めるまでの時間が短縮されます。

ページで実行される JavaScript の量を減らすには:

  • 使用していない JavaScript を遅延させる
  • 使用していないポリフィルを最小限に抑える

使用していない JavaScript を遅延させる

デフォルトでは、すべての JavaScript はレンダリングをブロックします。ブラウザは、外部の JavaScript ファイルにリンクするスクリプトタグを検出した場合、実行中の処理を一時停止し、その JavaScript をダウンロード、解析、コンパイル、実行する必要があります。そのため、ページに必要なコードまたはユーザー入力に応答するコードのみを読み込む必要があります。

Chrome DevTools の [カバレッジ] タブでは、ウェブページで JavaScript がどの程度使用されていないかを確認できます。

[カバレッジ] タブ。

使用していない JavaScript を削減するには:

  • バンドルを複数のチャンクにコード分割する
  • async または defer を使用して、サードパーティ スクリプトなど、重要でない JavaScript を遅延させる

コード分割とは、1 つの大きな JavaScript バンドルを、条件付きで読み込めるように小さなチャンクに分割する概念です(遅延読み込みとも呼ばれます)。新しいブラウザのほとんどは動的インポート構文をサポートしています。これにより、オンデマンドでモジュールを取得できます。

import('module.js').then((module) => {
  // Do something with the module.
});

ルートの変更やモーダルの表示など、特定のユーザー操作で JavaScript を動的にインポートすることで、最初のページ読み込みで使用されていないコードを必要な場合にのみ取得することができます。

動的インポート構文は、一般的なブラウザ サポートとは別に、さまざまなビルドシステムで使用できます。

  • webpackRollup、または Parcel をモジュール バンドラとして使用する場合は、動的インポートのサポートを活用します。
  • ReactAngularVue などのクライアント側フレームワークでは、コンポーネント レベルでの遅延読み込みが簡単になります。

クリティカル パスやスクロールせずに見える範囲のコンテンツに必要のないスクリプトには、コード分割とは別に、常に async または defer を使用してください。

<script defer src="…"></script>
<script async src="…"></script>

特別な理由がない限り、すべてのサードパーティ スクリプトは、デフォルトで defer または async のいずれかで読み込む必要があります。

使用していないポリフィルを最小限に抑える

最新の JavaScript 構文を使用してコードを作成し、最新のブラウザ API を参照する場合、古いブラウザで動作させるには、コードをトランスパイルしてポリフィルを含める必要があります。

サイトにポリフィルとトランスパイルされたコードを含めることに関する主なパフォーマンスの問題の 1 つは、新しいブラウザでは必要がない限り、ダウンロードする必要がないということです。アプリケーションの JavaScript のサイズを削減するには、使用していないポリフィルを可能な限り最小限に抑え、必要な環境だけにポリフィルの使用を制限します。

サイトでのポリフィルの使用を最適化するには:

  • Babel をトランスパイラとして使用する場合は、@babel/preset-env を使用して、ターゲットとするブラウザに必要なポリフィルのみを含めます。Babel 7.9 では、bugfixes オプションを有効にして、不要なポリフィルをさらに削減できます。
  • module/nomodule パターンを使用して 2 つの個別のバンドルを配信します(@babel/preset-envtarget.esmodules を介してこれをサポートします)。

    <script type="module" src="modern.js"></script>
    <script nomodule src="legacy.js" defer></script>
    

    Babel でコンパイルされた新しい ECMAScript 機能の多くは、JavaScript モジュールをサポートする環境ですでにサポートされています。これにより、実際にコードを必要とするブラウザにトランスパイルされたコードのみが使用されるようにするプロセスを簡素化できます。

デベロッパー ツール

FID の測定とデバッグには、さまざまなツールを利用できます。

レビューに協力してくれた Philip Walton、Kayce Basques、Ilya Grigorik、Annie Sullivan に感謝します。