ブログに戻る

INP は重いアナリティクスを罰する:なぜあなたのトラッカーはメインスレッドにいるのか

Interaction to Next Paint は最も多くのサイトが不合格になる Core Web Vital であり、フィールドデータは行動トラッキングスクリプトが主要因の一つであることを示している。解決策はメインスレッドに送る処理を減らすことだ。

Interaction to Next Paint(INP) は開発者が最も頻繁に不合格になる Core Web Vital であり、その原因はあなたの <head> の中にある。INP は 2024 年に安定した Core Web Vital となり、First Input Delay を置き換えた。そして FID には決して測れなかったものを測定する。すなわち、ページがその全ライフサイクルを通じて、あらゆるクリック・タップ・キー入力に対してどれだけ応答的であり続けるかだ。

この指標は、監視型アナリティクスが配信する種類の JavaScript に対して容赦がない。トラッカーがメインスレッド上で意味のある処理を行えば、ユーザーのすべての操作がそのコストを引き継ぐ。

INP が実際に測定するもの

INP はページ上のすべてのクリック・タップ・キー入力の 75 パーセンタイル における操作の遅延を報告する。200 ミリ秒以下が「良好」、200〜500 ミリ秒が「要改善」、500 ミリ秒超が「不良」だ。

各操作は 3 つのフェーズに分かれる。

  • 入力遅延(input delay) — メインスレッドが忙しいために、イベントハンドラーが開始すらできるようになるまでの時間。
  • 処理時間 — イベントハンドラーのコールバックが実行に要する時間。
  • 表示遅延(presentation delay) — コールバック完了からブラウザが次のフレームを描画するまでの時間。

重いアナリティクスはこの 3 つすべてを損なう。大きなバンドルを解析し、同意ロジックを評価し、タグマネージャーのキューをまとめ、あるいはフィンガープリント作成のためにレイアウトを読み取るトラッカーは、まさにユーザーが操作しようとするときにメインスレッドを占有する。

ロングタスクがその仕組みだ

ブラウザはタスクの実行中、操作を処理できない。50 ミリ秒 を超えるタスクはすべて ロングタスク であり、50 ミリ秒を超えた部分は、クリックがキューに溜まっていく死に時間だ。

十数個のベンダースクリプトを同期的に読み込み実行するタグマネージャーは、ロングタスクの連鎖を生む。ユーザーがクリックしても何も起きず、もう一度クリックする——そしてメインスレッドがようやく空いたとき、キューに溜まった入力が一斉に発火する。

これは理論ではない。HTTP Archive の 2024 年 Web Almanac は、ユーザー行動スクリプト(セッションリプレイ、ヒートマップ、行動アナリティクス)を載せたページがモバイルで良好な INP を達成したのはわずか 37%(デスクトップでは 60%)であることを明らかにした。これらのツールが要求する同意管理スクリプト(バナー)が INP に合格したのは、モバイルページのわずか 53% だった。「ユーザーを理解する」ためのはずの計測が、彼らの体験を能動的に劣化させている。

それを回避するアーキテクチャ

解決策は構造的なものであり、切り替えるフラグではない。コードを減らし、クリティカルパスから外しておく。

プライバシーファーストのトラッカーは、その解析と実行がロングタスクとして記録されないほど小さくできる。Monoid のトラッカーは約 2 KB で、依存関係がなく、cookie を設定せず、レイアウトも読み取らない——したがってフィンガープリント化するものも、重い実行処理もない。単一の keepalive リクエストでページビューを記録し、それ以外はアイドル状態を保つ。

fetch('/collect', {
  method: 'POST',
  keepalive: true,
  body: JSON.stringify({ site_id, path: location.pathname })
})

keepalive: true はメインスレッドを保持せずにリクエストをページより長く生存させるため、ナビゲーションや unload がビーコンを待ってブロックされることはない。

自分のスクリプトを実行する際は、処理のブロックごとにメインスレッドを譲り、キューに溜まった操作が実行できるようにする。

async function processQueue(items) {
  for (const item of items) {
    handle(item)
    if (navigator.scheduling?.isInputPending?.()) {
      await scheduler.yield()
    }
  }
}

scheduler.yield() はロングタスクを分割し、優先度を持って再開するため、サードパーティスクリプトがあなたの継続処理よりキューの前に割り込むことはできない。

ラボではなくフィールドで測定せよ

Lighthouse は INP を報告できない。INP は実在の人間が操作して初めて存在するからだ。ラボツールは推定するだけで、応答性を測定しない。

フィールドデータを使う。Chrome User Experience Report(CrUX) はオプトインした Chrome ユーザーの実際の操作を集計し、Search Console の Core Web Vitals レポートは同じデータを URL グループごとに提示する。CrUX の INP は良好だがラボの Total Blocking Time が高いなら余裕がある。CrUX の INP が不良なら、ユーザーはすでにそれを感じている。

プライバシーの論拠とパフォーマンスの論拠はここで一致する。ストレージを読まず、フィンガープリントを作らず、ベンダーの連鎖を配信しないトラッカーは、メインスレッド上でほとんど何もすることがない——だからこそ、あなたの INP に決して現れない。監視は信頼だけでなく、ミリ秒においても高くつく。

出典

Comments

Loading comments…