First Input Delay を最適化する
ユーザーによる操作により速く応答する方法。
クリックしても反応がありません。なぜこのページを操作できないのでしょうか?😢
First Contentful Paint (視覚コンテンツの初期表示時間、FCP) と Largest Contentful Paint (最大視覚コンテンツの表示時間、LCP) は、どちらもコンテンツがページ上で視覚的にレンダリング (描画) されるまでにかかる時間を測定する指標です。描画にかかる時間も重要ではありますが、これは読み込みの応答性、つまりユーザーの操作に対するページの応答の速さを示すものではありません。
First Input Delay (FID) は、サイトのインタラクティブ性や応答性についてのユーザーの第一印象を測定する Core Web Vitals 指標です。ユーザーが最初にページに操作してからブラウザーが実際にその操作に応答するまでの時間を測定します。FID はフィールド指標であり、ラボ環境ではシミュレートできません。応答の遅延を測定するには、実際のユーザーによる操作が必要です。
ラボ環境での FID の予測には、Total Blocking Time (合計ブロック時間、TBT) をお勧めしています。この 2 つの指標の測定対象は異なりますが、通常 TBT の改善は FID の改善に相当します。
FID が悪化する場合の主な原因には、重い JavaScript の実行が挙げられます。Web ページ上での JavaScript の解析方法、コンパイル方法、実行方法を最適化することにより、FID を直接的に短縮することができます。
重い JavaScript の実行 #
ブラウザーは、メイン スレッドで JavaScript を実行している最中にはほとんどのユーザー入力に応答することができません。言い換えれば、メイン スレッドがビジー状態の間はブラウザーがユーザーの操作に応答することはできません。これを改善するには、以下の方法を行います。
長く時間がかかっているタスクを分割する #
単一のページで読み込まれる JavaScript の量の削減をすでに試している場合には、長時間に渡って実行されるコードを比較的小さな非同期タスクへと分割する方法が有効となる可能性があります。
長く時間がかかっているタスクとは、ユーザーが UI の応答がが悪いと感じる可能性のある JavaScript の実行時間のことを指します。メイン スレッドを 50 ミリ秒以上に渡ってブロックし続けるコードは、すべて長く時間がかかっているタスクとして分類されます。長く時間がかかっているタスクは、潜在的な JavaScript の肥大化 (ユーザーがその場で必要とする以上のものを読み込み、実行すること) の兆候を示しています。長く時間がかかっているタスクを分割することにより、サイトの入力遅延を削減することができます。

コード分割や、長く時間がかかっているタスクの分割などのベスト プラクティスを採用することにより、FID は顕著に改善されるはずです。TBT はフィールド指標ではありませんが、Time to Interactive (操作可能になるまでの時間、TTI) および FID の両指標が最終的に改善されるまでの進捗状況の確認に役立ちます。
操作に対する準備状況にページを最適化する #
JavaScript に大きく依存している Web アプリケーションで FID や TBT のスコアが悪化する原因には、いくつかのものが考えられます。
ファーストパーティー スクリプトの実行によって操作に対する準備状況に遅延が生じる #
- JavaScript のサイズの肥大化、実行時間の長期化、非効率なチャンキングは、ユーザーの入力に対するページの応答時間を遅らせ、FID、TBT、TTI に影響を与えます。コードや機能のプログレッシブ読み込みはこの作業を分散させ、操作に対する準備状況を改善させます。
- サーバー側でレンダリングされたアプリは、一見すぐに画面にピクセルが描画されるように見えるかもしれませんが、大量のスクリプトの実行によってユーザーの操作がブロックされる場合もあるので注意が必要です (イベント リスナーを接続するためのリハイドレーションなど)。ルートベースのコード分割が使用されている場合、これには数百ミリ秒、場合によっては数秒かかる可能性があります。より多くのロジックをサーバー側に移行するか、ビルド時に静的にコンテンツを生成することを検討してください。
次に示すのは、あるアプリケーションのファーストパーティー スクリプトの読み込みを最適化する前後での TBT スコアの比較です。必要のないコンポーネントのために読み込み (および実行) にコストがかかっていたスクリプトをクリティカル パスから除外することにより、ユーザーはより早くページを操作できるようになりました。

データフェッチが操作に対する準備状況の様々な側面に影響を与える可能性がある #
- カスケーディング フェッチのウォーターフォール (例: コンポーネントで使用される JavaScript やデータのフェッチ) での待機は、操作の遅延時間に影響を及ぼす可能性があります。カスケーディング データ フェッチへの依存は、最小限に抑えるようにしましょう。
- サイズの大きいインラインのデータストアは、HTML の解析にかかる時間を増大させ、描画や操作の指標に影響を与えます。クライアント側で後処理が必要なデータの量を最小限に抑えるようにしましょう。
サードパーティ スクリプトの実行も操作の遅延時間に悪影響を及ぼす可能性がある #
- ネットワークをビジー状態にし、メイン スレッドを定期的に応答不可の状態にする可能性があるサードパーティ製のタグやアナリティクスが多くのサイトで使用されていますが、これらは操作の遅延時間に影響を及ぼします。サードパーティ製のコードについてはオンデマンドでの読み込みをご検討ください (たとえば、スクロールしてビューポートの範囲内に含まれるまで、Below the fold (ビロウ・ザ・フォールド、スクロールせずに閲覧可能なサイトのファースト ビュー以外の部分を指す) にある広告を読み込まないようにするなど)。
- 場合によっては、メイン スレッドでの優先度や帯域幅の観点からサードパーティ スクリプトがファーストパーティ スクリプトよりも先に実行される可能性があり、ページが操作に応答できるようになるまでの時間にも遅延が生じる場合があります。ユーザーにとって最も価値があると思われるものを優先的に読み込むようにしましょう。
Web Worker を使用する #
メイン スレッドのブロックは、入力遅延を引き起こす主な原因の 1 つです。Web Worker は、JavaScript をバックグラウンドのスレッドで実行できるようにします。UI 以外の処理を別のワーカー スレッドに移すことでメイン スレッドのブロック時間を短縮し、結果的に FID を改善させることができます。
運営するサイトで Web Worker をより簡単にご利用いただくために、以下のライブラリの使用をご検討ください。
- Comlink:
postMessage
を抽象化して使いやすくしたヘルパー ライブラリ - Workway: 汎用的な Web Worker エクスポーター
- Workerize: モジュールを Web Worker に移動
JavaScript の実行にかかる時間を短縮する #
ページ上の JavaScript の量を制限することで、ブラウザーが JavaScript コードを実行するために必要となる時間を短縮することができます。これにより、ユーザーの操作に対するブラウザーの応答時間を高速化することができます。
ページ上で実行される JavaScript の量を減らす方法には、以下のものがあります。
- 使用されていない JavaScript を先送りする
- 使用されていないポリフィルを最小限に抑える
使用されていない JavaScript を先送りする #
デフォルトでは、すべての JavaScript はレンダリングをブロックします。ブラウザーは外部の JavaScript ファイルにリンクされているスクリプト タグに遭遇すると処理を中断し、その JavaScript のダウンロード、解析、コンパイル、実行を行わなければならなくなります。そのため、ページの処理やユーザーの入力に対する応答に必要なコードのみを読み込む必要があります。
Chrome DevTools の Coverage (カバレッジ) タブでは、Web ページで使用されていない JavaScript がどの程度存在しているかを確認することができます。

使用されていない JavaScript を削減する方法には、以下のものがあります。
- 複数のチャンクへとバンドルのコードを分割する
- サードパーティ スクリプトを含む重要でない JavaScript を
async
またはdefer
を使用して先送りする
コード分割とは、1 つの大きな JavaScript バンドルを条件付きでの読み込みが可能な小さなチャンクへと分割することを意味します (レイジーロードとも呼ばれています)。最新のブラウザーのほとんどが動的なインポート構文をサポートしており、モジュールを必要に応じてフェッチすることができます。
import('module.js').then((module) => {
// モジュールを使用して何かを実行します。
});
特定のユーザー操作 (ルートの変更やモーダル ウィンドウの表示など) に関する JavaScript を動的にインポートすることにより、最初のページ読み込みに使用されないコードを必要な場合にのみ取得できるようになります。
一般的なブラウザーのサポートに加えて、動的なインポート構文は様々なビルド システムでも使用が可能です。
- モジュール バンドラーとして webpack、Rollup、Parcel を使用している場合には、それらが提供する動的インポートのサポートをご利用ください。
- React、Angular、Vue のようなクライアントサイド フレームワークでは、コンポーネントレベルでのレイジーロードを容易にするための抽象化を提供しています。
コード分割以外にも、クリティカル パスや Above the fold (アバブ・ザ・フォールド、スクロールせずに閲覧可能なサイトのファースト ビューを指す) のコンテンツに必要のないスクリプトについては常に 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-env
もtarget.esmodules
を介してこれをサポートしています)。<script type="module" src="modern.js"></script>
<script nomodule src="legacy.js" defer></script>JavaScript モジュールをサポートしている環境であれば、Babel を使用してコンパイルされた新しい ECMAScript の機能の多くはすでにサポートされています。そのため、この手法を用いることにより、トランスパイルされたコードのみが実際にそれらを必要としているブラウザーに使用されていることを確認するプロセスを簡素化することができます。
開発者ツール #
FID を測定またはデバッグするためのツールは、以下のように様々なものが用意されています。
FID はフィールド指標であるため、Lighthouse 6.0 には FID のサポートは含まれていません。ただし、その代替指標として Total Blocking Time (TBT) を使用することができます。TBT を改善することができる最適化手法であれば、実際のユーザー環境での FID も改善することができるはずです。
Chrome User Experience Report は、オリジンレベルで集計された実際の環境での FID 値を提供します。
レビューしていただいた Philip Walton、Kayce Basques、Ilya Grigorik、Annie Sullivan には、心より感謝申し上げます。