入力遅延を最適化する

入力遅延の概要と、入力遅延を短縮してインタラクティビティを高速化する方法をご紹介します。

ウェブ上でのインタラクションは複雑なものであり、ブラウザ内でさまざまなアクティビティが発生してそれを促進します。どのツールにも共通するのは、イベント コールバックの実行を開始する前に入力遅延が発生することです。このガイドでは、入力遅延の概要と、入力遅延を最小限に抑えてウェブサイトの操作を高速化するための方法について説明します。

入力遅延とは何ですか?

入力遅延は、ユーザーが最初にページを操作(画面のタップ、マウスのクリック、キーの押下など)してから、その操作に対するイベント コールバックの実行が開始されるまでの時間です。すべてのインタラクションは、ある程度の入力遅延を伴います。

入力遅延の簡略図。左にはマウスのカーソルの線画があり、その後ろにスターバーストがあり、操作の開始を示している。右は歯車の線画で、インタラクションのイベント ハンドラの実行が開始するタイミングを示しています。中間のスペースは、中かっこで入力遅延として示されます。
入力遅延の仕組み。入力をオペレーティング システムが受け取った場合、操作を開始する前に、その入力をブラウザに渡す必要があります。これにはある程度の時間がかかりますが、既存のメインスレッドの処理によって増やすことができます。

入力遅延の一部の発生は避けられません。オペレーティング システムが入力イベントを認識してブラウザに渡すまでには、常にある程度の時間がかかります。しかし、多くの場合、入力遅延のこの部分は目立たないこともあります。また、ページ自体で何かしらの理由で入力遅延が長くなり、問題を引き起こすこともあります。

入力遅延についての考え方

一般的には、ユーザーのデバイスに関係なく、ウェブサイトが Interaction to Next Paint(INP)指標の「良好」しきい値を満たす可能性を最大限に高めるには、インタラクションのあらゆる部分をできるだけ短くする必要があります。入力遅延を常時チェックすることは、しきい値を満たすための一部にすぎません。

したがって、INP の「良好」しきい値を満たすために、可能な限り短い入力遅延を目指します。ただし、入力遅延を完全に排除することはできません。ユーザーがページを操作するときにメインスレッドで過度な作業を行わない限り、問題を回避するために入力遅延は十分に小さくする必要があります。

入力遅延を最小限に抑える方法

前述のように、ある程度の入力遅延は避けられませんが、その一方で、ある程度の入力遅延は回避できます。入力の遅延が長い場合は、次の点に注意してください。

過剰なメインスレッド処理を開始するタイマーの繰り返しを避ける

JavaScript でよく使用されるタイマー関数は、入力遅延の原因となる setTimeoutsetInterval の 2 つです。この 2 つの違いは、setTimeout は指定された時間が経過した後にコールバックを実行するようにスケジュールする点です。一方、setInterval は、n ミリ秒ごとに、または clearInterval でタイマーが停止するまで、コールバックを実行するようにスケジュールします。

setTimeout 自体は問題ではありません。実際、長いタスクの回避に役立ちます。ただし、タイムアウトが発生するタイミングと、タイムアウト コールバックの実行時にユーザーがページを操作しようとしたかどうかによって異なります。

また、setTimeout はループで実行することも、再帰的に実行することもできます。その場合、setInterval のように動作しますが、前のイテレーションが完了するまで次のイテレーションをスケジュールしないのが望ましいです。つまり、setTimeout が呼び出されるたびにループがメインスレッドに譲られますが、コールバックが過剰な処理を行わないように注意する必要があります。

setInterval は一定の間隔でコールバックを実行するため、インタラクションの妨げになる可能性が高くなります。これは、ユーザー操作を妨げる可能性がある 1 回限りのコールバックである setTimeout 呼び出しの 1 回のインスタンスとは異なり、setInterval は繰り返し発生する性質により操作の妨げになり、操作の入力遅延が増加するためです。

入力遅延を示す Chrome DevTools のパフォーマンス プロファイラのスクリーンショット。タイマー関数によって呼び出されるタスクは、ユーザーがクリック操作を開始する直前に発生します。ただし、タイマーは入力遅延を延長して、インタラクションのイベント コールバックが本来よりも遅く実行されます。
Chrome DevTools のパフォーマンス パネルに表示されている入力遅延の原因となっている、前回の setInterval 呼び出しで登録されたタイマー。入力遅延が増えると、インタラクションのイベント コールバックが本来よりも遅れて実行されます。

タイマーがファーストパーティ コードで発生している場合は、そのタイマーを制御できます。それらが必要かどうかを評価するか、できる限り処理を減らすために最善を尽くします。ただし、サードパーティのスクリプトのタイマーは状況が異なります。サードパーティのスクリプトの動作を制御できない場合、サードパーティのコードのパフォーマンスの問題を修正するには、多くの場合、関係者と連携して特定のサードパーティのスクリプトが必要かどうかを判断します。必要な場合は、サードパーティのスクリプト ベンダーと連絡を取り、ウェブサイトのパフォーマンスの問題を修正するために何ができるかを判断します。

時間のかかるタスクを避ける

長い入力遅延を軽減する 1 つの方法は、長いタスクを回避することです。インタラクション中にメインスレッドをブロックするようなメインスレッドの処理が過剰になると、長いタスクが完了するまでの入力遅延が増加します。

タスクが入力遅延を延長する時間の可視化。上部では、1 つの長いタスクの実行直後にインタラクションが発生するため、入力遅延が大幅に発生し、イベントのコールバックが本来よりもかなり遅れて実行されます。一番下では、インタラクションはほぼ同時に発生しますが、長いタスクは ylded によっていくつかの小さなタスクに分割されるため、インタラクションのイベント コールバックがより早く実行されます。
タスクが長すぎてブラウザがインタラクションにすばやく応答できない場合と、長いタスクが小さなタスクに分割された場合にインタラクションがどうなるかを示す図。

タスクで行う作業量を最小限に抑えることに加え、メインスレッドで行う作業は常に可能な限り少なくする必要があります。それに加えて、長いタスクを分割することで、ユーザー入力に対する応答性を向上させることができます。

インタラクションの重複に注意する

INP の最適化で特に難しいのは、重複するインタラクションがある場合です。インタラクションの重複とは、ある要素に対してインタラクションを行った後、最初のインタラクションで次のフレームがレンダリングされる前に、ページに対して別のインタラクションを行うことを指します。

タスクがオーバーラップして長い入力遅延が生じるタイミングの図。この図では、クリック操作とキーダウン操作が重なって、キーダウン操作の入力遅延が長くなっています。
Chrome の DevTools のパフォーマンス プロファイラで、2 つの同時インタラクションを可視化した図。最初のクリック操作でのレンダリング処理により、後続のキーボード操作での入力遅延が発生します。

インタラクションの重複のソースは、ユーザーが短期間に多数のインタラクションを行っているような単純な場合もあります。これは、ユーザーがフォームのフィールドに入力するときに発生することがあります。多くのキーボード操作が非常に短時間で行われる可能性があります。バックエンドにネットワーク リクエストを行う一般的なオートコンプリート フィールドの場合のように、重要なイベントの処理に特にコストがかかる場合は、次の 2 つの方法があります。

  • 入力のデバウンスを行って、一定期間内にイベント コールバックを実行する回数を制限することを検討します。
  • メインスレッドが fetch コールバックの処理に輻輳しないように、AbortController を使用して送信 fetch リクエストをキャンセルします。注: AbortController インスタンスの signal プロパティは、イベントを中止するためにも使用できます。

インタラクションの重複によって入力遅延が増加するもう 1 つの原因は、負荷の高いアニメーションです。特に、JavaScript のアニメーションでは多数の requestAnimationFrame 呼び出しが発生し、ユーザーの操作を妨げる可能性があります。この問題を回避するには、可能な限り CSS アニメーションを使用して、コストが高くなる可能性のあるアニメーション フレームをキューに入れないようにします。ただし、その場合、アニメーションがメインスレッドではなく主に GPU スレッドとコンポジタ スレッドで実行されるように、非合成アニメーションは避けます

おわりに

入力遅延がインタラクションの実行時間の大半を占めるとは限りませんが、インタラクションのあらゆる部分が費やす時間は短縮できることを理解することが重要です。入力遅延が長い場合は、短縮する方法があります。タイマーのコールバックの繰り返しを回避したり、長いタスクを分割したり、操作の重複の可能性を認識したりすると、入力の遅延を減らし、ウェブサイトのユーザーのインタラクティビティを高速化できます。

Unsplash より提供: Erik Mclean のヒーロー画像