ウェブサイトの Interaction to Next Paint を最適化する方法をご確認ください。
Interaction to Next Paint(INP)は、ユーザーによるページアクセスの全期間中に発生するすべての対象インタラクションのレイテンシをモニタリングすることで、ユーザー インタラクションに対するページの全体的な応答性を評価するための安定した Core Web Vitals 指標です。最終的な INP 値は、最大時間を要したことがモニタリングされたインタラクションであり、外れ値は無視される場合があります。
優れたユーザー エクスペリエンスを提供するため、ウェブサイトは Interaction to Next Paint を 200 ミリ秒以下に抑えるよう努める必要があります。ほとんどのユーザーが閲覧する際に目標値が達成されるよう、モバイル デバイスとデスクトップ デバイスでページ読み込みの75 パーセンタイルを測定することをおすすめします。
ウェブサイトによっては、インタラクティブな要素がほとんどない、またはまったくないページ(テキストと画像がほとんどで、インタラクティブな要素がほとんどないページなど)もあります。テキスト エディタやゲームなどのウェブサイトでは、数百、数千ものインタラクションが発生することもあります。いずれの場合も、INP が高いとユーザー エクスペリエンスが損なわれるリスクがあります。
INP を改善するには時間と労力が必要ですが、その結果、ユーザー エクスペリエンスが向上します。このガイドでは、INP を改善するための方法について説明します。
INP が低い原因を特定する
操作の遅延を修正する前に、ウェブサイトの INP が低下しているのか、改善の必要があるのかを示すデータが必要です。これらの情報を把握したら、ラボに進み、遅いインタラクションの診断を開始し、解決策に向けて取り組みます。
フィールドで遅いインタラクションを見つける
理想的には、INP の最適化の取り組みはフィールド データから始めるのが理想的です。実際のユーザー モニタリング(RUM)プロバイダから提供されるフィールド データは、ページの INP 値だけでなく、INP 値そのものの原因となった特定のインタラクション、ページの読み込み中または読み込み後のインタラクションの種類(クリック、キー入力、タップ)、その他の有用な情報を示すコンテキスト データも有用です。
RUM プロバイダに依存せずにフィールド データを取得している場合は、INP フィールド データ ガイドで推奨されているように、PageSpeed Insights で Chrome ユーザー エクスペリエンス レポート(CrUX)を使用して不足しているデータを補完することをおすすめします。CrUX は Core Web Vitals プログラムの公式データセットであり、INP を含む数百万ものウェブサイトの指標の概要を提供します。ただし、CrUX では多くの場合、問題の分析に役立つ RUM プロバイダから取得したコンテキスト データが提供されません。そのため、可能な限り RUM プロバイダを使用するか、独自の RUM ソリューションを実装して CrUX で利用可能な機能を補完することをおすすめします。
ラボでの遅いインタラクションの診断
インタラクションが遅いことを示すフィールド データが得られたら、ラボでテストを開始するのが理想的です。フィールドデータがない場合は、ラボで遅いインタラクションを見つけるための戦略がいくつかあります。このような戦略には、一般的なユーザーフローを追跡してその過程でインタラクションをテストする方法や、読み込み中(多くの場合、メインスレッドが最も負荷が高いとき)にページを操作して、ユーザー エクスペリエンスの重要な部分で遅いインタラクションが発生しているかどうかを特定する方法などがあります。
インタラクションを最適化する
遅いインタラクションを見つけてラボで手動で再現できる場合は、次に最適化を行います。インタラクションには次の 3 つのフェーズがあります。
- 入力遅延: ユーザーがページの操作を開始したときに始まり、操作のイベント コールバックの実行が開始されたときに終了します。
- 処理時間。イベント コールバックの実行が完了するまでにかかる時間が含まれます。
- プレゼンテーションの遅延。ブラウザが操作の視覚的な結果を含む次のフレームを表示するのにかかる時間。
これら 3 つのフェーズの合計がインタラクション レイテンシの合計です。インタラクションの各フェーズは、インタラクションの合計レイテンシに一定の時間を要します。そのため、インタラクションの各部分を最適化して、実行時間を最小限に抑えることが重要です。
入力遅延を特定して短縮する
ユーザーがページを操作すると、その操作の最初の部分が入力遅延です。ページの他のアクティビティによっては、入力遅延がかなり長くなる場合があります。この原因としては、メインスレッドでのアクティビティ(スクリプトの読み込み、解析、コンパイルなど)、フェッチ処理、タイマー関数、または短時間に連続して発生し互いに重複する他の操作が原因の可能性があります。
インタラクションの入力遅延の原因がどのようなものであっても、インタラクションができるだけ早くイベント コールバックの実行を開始できるように、入力遅延を最小限に抑える必要があります。
起動時のスクリプト評価と長時間タスクの関係
ページのライフサイクルにおけるインタラクティビティの重要な要素は、起動時です。ページが読み込まれると、最初はレンダリングされますが、ページがレンダリングされたからといって、ページの読み込みが完了したわけではないことに注意してください。ページが完全に機能するために必要なリソースの数によっては、読み込み中にユーザーがページを操作しようとする可能性があります。
ページの読み込み中にインタラクションの入力遅延を長くする要因の一つが、スクリプトの評価です。JavaScript ファイルがネットワークから取得された後、その JavaScript を実行する前にブラウザで行う作業がまだあります。その作業には、スクリプトを解析して構文が有効であることを確認する、バイトコードにコンパイルする、最後に実行するなどがあります。
スクリプトのサイズによっては、この処理によってメインスレッドで長時間実行タスクが発生し、ブラウザが他のユーザー操作に応答するまでに遅延が生じる可能性があります。ページ読み込み中にユーザー入力に応答し続けるには、ページ読み込み中に長時間のタスクが発生する可能性を減らし、ページの応答性を維持するためにできることを理解することが重要です。
イベント コールバックを最適化する
入力遅延は、INP が測定する最初の部分にすぎません。また、ユーザー操作に応じて実行されるイベント コールバックが可能な限り迅速に完了するようにする必要があります。
頻繁にメインスレッドに移る
イベント コールバックを最適化する際の一般的なアドバイスは、コールバックでできるだけ少ない処理を行うことです。ただし、インタラクションのロジックが複雑で、実行する作業をわずかに減らすことができない場合もあります。
ウェブサイトがこれに該当する場合は、次に、イベント コールバックの処理を個別のタスクに分割することを試します。これにより、集約処理が長時間のタスクになってメインスレッドをブロックすることを防ぐことができます。これにより、メインスレッドで待機していた他のインタラクションの実行が早くなります。
setTimeout
は、渡されたコールバックが新しいタスクで実行されるため、タスクを分割する方法の 1 つです。setTimeout
を単独で使用することも、より人間工学的なイーリングのために、その使用を別の関数に抽象化することもできます。
無差別に譲渡するほうが、まったく譲渡しないよりはましです。ただし、メインスレッドに譲渡する方法には、より微妙な方法があります。これは、ユーザー インターフェースを更新するイベント コールバックの直後にのみ譲渡し、レンダリング ロジックをより早く実行できるようにします。
レンダリング作業を早期に開始できるようにする
より高度な譲渡手法では、イベント コールバックのコード構造を変更して、実行される処理を次のフレームのビジュアル アップデートに必要なロジックに限定します。その他の処理は後続のタスクに延期できます。これにより、コールバックが軽量で機敏に保たれるだけでなく、イベント コールバック コードの視覚的な更新がブロックされなくなるため、操作のレンダリング時間も短縮されます。
たとえば、入力した内容に応じてテキストを書式設定するだけでなく、UI の他の部分(単語数、スペルミスのハイライト表示、その他の重要な視覚的なフィードバックなど)も更新するリッチテキスト エディタについて考えてみましょう。また、アプリケーションは、書き込んだ内容を保存して、離れてから戻ってきたときに作業内容が失われないようにする必要があります。
この例では、ユーザーが入力した文字に応じて、次の 4 つの処理を行う必要があります。ただし、次のフレームを表示する前に完了する必要があるのは最初の項目のみです。
- ユーザーが入力した内容でテキスト ボックスを更新し、必要な書式を適用します。
- 現在の単語数を表示する UI の部分を更新します。
- ロジックを実行して誤字脱字を確認します。
- 最新の変更を(ローカルまたはリモート データベースに)保存します。
次のようなコードになります。
textBox.addEventListener('input', (inputEvent) => {
// Update the UI immediately, so the changes the user made
// are visible as soon as the next frame is presented.
updateTextBox(inputEvent);
// Use `setTimeout` to defer all other work until at least the next
// frame by queuing a task in a `requestAnimationFrame()` callback.
requestAnimationFrame(() => {
setTimeout(() => {
const text = textBox.textContent;
updateWordCount(text);
checkSpelling(text);
saveChanges(text);
}, 0);
});
});
次の図は、クリティカルでない更新を次のフレームまで遅らせることで、処理時間を短縮し、全体的なインタラクション レイテンシを低減できることを示しています。
上のコード例で requestAnimationFrame()
呼び出し内で setTimeout()
を使用するのは、やや難解ですが、すべてのブラウザで機能し、重要なコードが次のフレームをブロックしないようにする効果的な方法です。
レイアウトのスラッシングを回避する
レイアウト スラッシング(強制同期レイアウト)は、レイアウトが同期的に行われるため、レンダリング パフォーマンスに問題が生じる現象です。これは、JavaScript でスタイルを更新してから、同じタスクで読み取るときに発生します。JavaScript には、レイアウト スラッシングを引き起こす可能性があるプロパティが多数あります。
レイアウト スラッシングはパフォーマンスのボトルネックです。スタイルを更新してから JavaScript でそのスタイルの値をすぐにリクエストすると、ブラウザは同期レイアウト処理を強制的に行うことになります。この処理は、イベント コールバックの実行が完了した後で非同期に実行することもできます。
表示の遅延を最小限に抑える
インタラクション マークの表示遅延は、インタラクションのイベント コールバックの実行が完了してから、ブラウザが結果の視覚的な変更を示す次のフレームをペイントできるまでの時間です。
DOM サイズを最小限に抑える
ページの DOM が小さい場合、通常はレンダリング作業はすぐに完了します。ただし、DOM が非常に大きくなると、DOM のサイズが大きくなるにつれてレンダリング処理がスケールする傾向があります。レンダリング作業と DOM サイズの関係はリニアではありませんが、DOM が大きいほど、レンダリングに必要な作業量は多くなります。大きな DOM は、次の 2 つの場合に問題になります。
- ページの最初のレンダリング中。DOM が大きい場合、ページの初期状態をレンダリングするために多くの作業が必要になります。
- ユーザー操作に応答する場合。DOM が大きい場合、レンダリングの更新に非常に時間がかかり、ブラウザが次のフレームを表示するまでに時間がかかります。
ただし、DOM が大きい場合、大幅に削減できないこともあります。DOM のサイズを縮小するには、DOM をフラット化する、ユーザーによる操作中に DOM に追加して初期 DOM サイズを小さくするなど、いくつかのアプローチがありますが、これらの手法では限界があります。
content-visibility
を使用して画面外の要素のレンダリングを遅延させる
ページの読み込み時とユーザー操作に応じたレンダリング処理の両方の量を制限する方法の一つは、CSS content-visibility
プロパティを使用することです。これは、ビューポートに近づくにつれて要素を遅延レンダリングする効果があります。content-visibility
を効果的に使用するには多少の練習が必要ですが、レンダリング時間が短くなり、ページの INP が向上する可能性があるかどうかを調べることをおすすめします。
JavaScript を使用して HTML をレンダリングする場合のパフォーマンス コストに注意する
HTML がある場合は HTML の解析が行われます。ブラウザが HTML を DOM に解析し終えると、スタイルを適用し、レイアウト計算を実行して、そのレイアウトをレンダリングする必要があります。これは避けられないコストですが、HTML のレンダリング方法が重要です。
サーバーが HTML を送信すると、HTML はストリームとしてブラウザに届きます。ストリーミングとは、サーバーからの HTML レスポンスがチャンクで届くことを意味します。ブラウザは、ストリーミングのチャンクが到着するたびにそのチャンクを増分的に解析し、ビット単位でレンダリングすることで、ストリーミングの処理方法を最適化します。これは、ページの読み込み中にブラウザが暗黙的に定期的に自動的に譲渡するパフォーマンスの最適化であり、無料で利用できます。
ウェブサイトに初めてアクセスする際には、必ず一定量の HTML が使用されますが、一般的なアプローチでは、最初は最小限の HTML から始めて、JavaScript を使用してコンテンツ領域にコンテンツを入力します。その後のコンテンツ領域の更新も、ユーザー操作の結果として行われます。これは通常、シングルページ アプリケーション(SPA)モデルと呼ばれます。このパターンの欠点の 1 つは、クライアントで JavaScript を使用して HTML をレンダリングすると、その HTML を作成するための JavaScript 処理のコストが発生するだけでなく、HTML の解析とレンダリングが完了するまでブラウザが譲渡しないことです。
ただし、SPA を使用していないウェブサイトであっても、おそらく JavaScript を介した HTML レンダリングが相互作用によって生じる可能性があることを覚えておいてください。通常は、クライアントで大量の HTML をレンダリングしていない限り、これは問題ありません。大量の HTML をレンダリングすると、次のフレームの表示が遅れる可能性があります。ただし、ブラウザで HTML をレンダリングするこのアプローチがパフォーマンスに与える影響と、JavaScript を介して大量の HTML をレンダリングする場合に、ウェブサイトのユーザー入力に対する応答性にどのような影響を与えるかを理解することが重要です。
まとめ
サイトの INP を改善するには、反復的なプロセスが必要です。フィールドで遅いインタラクションを修正すると、特にウェブサイトに多くのインタラクションが含まれている場合、他の遅いインタラクションも見つかり、それらも最適化する必要があります。
INP を改善する鍵は、粘り強さです。やがては、提供するエクスペリエンスにユーザーが満足する場所にページの応答性が向上するはずです。また、ユーザー向けの新機能を開発する際に、ユーザー固有のインタラクションを最適化するために、同じプロセスを繰り返す必要がある可能性もあります。時間と労力はかかりますが、その労力は必ず報われます。
David Pisnoy 著「Unsplash」のヒーロー画像。Unsplash ライセンスに従って編集。