Soft Navigations:ブラウザ流に SPA のパフォーマンスを計測する
Chrome のソフトナビゲーション・ヒューリスティックにより、Core Web Vitals がクライアントサイドのルート変更にひも付くようになりました。API の仕組みと、監視なしで計測する方法を解説します。
この十年、Core Web Vitals は訪問者が最初に読み込んだページしか表現してきませんでした。シングルページアプリケーションでは、その後のルート変更はすべて指標から見えませんでした。Chrome の soft navigations 実験はこの空白を埋め、プライバシー優先のアナリティクスが実ユーザーのパフォーマンスをどう計測すべきかを変えます。
ソフトナビゲーションが埋める死角
従来の hard navigation はドキュメントをアンロードし、新しいドキュメントを読み込みます。ブラウザはパフォーマンスのタイムラインをリセットし、新しい LCP を発火させ、CLS と INP をゼロから数え始めます。CrUX のようなフィールドツールは、すべてをその 1 つの URL に帰属させます。
SPA はこのようには動きません。初回読み込みの後、React Router、Next.js の App Router、SvelteKit は History API を使ってコンテンツをその場で差し替えます。ドキュメントはアンロードされないため、新しいパフォーマンスのタイムラインは始まりません。ユーザーが 10 個の「ページ」をクリックして回っても、各 Core Web Vital は入口の URL に固定されたままです。
SPA を監査したことがある人なら誰でも知っている結果です。ランディングのルートは速く見え、アプリの奥深くにある遅いインタラクションはデータに一切現れません。
Chrome がソフトナビゲーションを検出する仕組み
Chrome のヒューリスティックは、ソフトナビゲーションを記録する前に、次の 3 つが順番に起きることを要求します。
- ナビゲーションがユーザーのインタラクション(クリックまたはキー押下)によって開始される。
- URL が History API または Navigation API によって変更される。
- インタラクションの後に DOM の変更が続き、既存の DOM 要素を変更する。
このシーケンスは意図的に厳格です。アナリティクス用のバックグラウンドの pushState、自動で進むカルーセル、インタラクションを伴わない URL 変更は条件を満たしません。この厳密さが重要です。ソフトナビゲーションが、付随的なスクリプトの動きではなく、人間が実際に行った何かに対応することを意味するからです。
API の表面
ソフトナビゲーションは、専用のエントリタイプを使って標準の PerformanceObserver から表面化します。observer 単位でオプトインします。
new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
console.log(entry.name, entry.startTime);
}
}).observe({ type: "soft-navigation", buffered: true });
さらに重要なのは、他のパフォーマンスエントリが navigationId を得る点です。ソフトナビゲーションの後に発行された LCP、layout-shift、event-timing の各エントリは新しい ID を持つため、各 Core Web Vital を、ユーザーが実際にいたルートへ再帰属させられます。
new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
// entry.navigationId はこの LCP を特定のソフトナビゲーションに結び付ける
send({ metric: "LCP", value: entry.startTime, navId: entry.navigationId });
}
}).observe({ type: "largest-contentful-paint", buffered: true });
現時点では、これは chrome://flags/#soft-navigation-heuristics と origin trial の背後にあり、CrUX での露出は限定的です。安定版 Chrome ではまだデフォルトではないため、フィールドの数値は権威あるものではなく、方向性を示すものとして扱ってください。web-vitals ライブラリは reportSoftNavs オプションで同じデータを公開しており、これが observer を手書きせずに組み込む実用的な方法です。
なぜこれがプライバシー優先のアナリティクスに属するのか
ソフトナビゲーションのデータは純粋なタイミングです。識別子も cookie も、ページセッションを超えて残るものも含みません。すなわち、所要時間、おおまかな要素タイプ、ルートのパスだけです。これはまさに、cookie を使わないツールが同意の仕組みに触れずに収集できる種類のシグナルです。
トラッカーはすでに history.pushState にフックして SPA のルート変更を数えています。ソフトナビゲーションは同じフックを精緻化します。単にルートは変わったかを問うだけでなく、これはインタラクション駆動のナビゲーションだったか、そのパフォーマンスはどうだったかを問えます。パフォーマンスエントリは既存の pageview ビーコンとともに /collect へ送られ、2 KB 未満のスクリプトを重くせず、日次ハッシュの識別モデルを壊すこともなく、フィールド計測を加えます。
避けるべき罠は、ソフトナビゲーションをより多く集める口実にすることです。一部の RUM ベンダーは、この新しい帰属を使って、セッションをまたいでユーザーごとのナビゲーション経路を縫い合わせます。それはまさに、cookie を使わないアナリティクスが拒否するために存在する監視のパターンです。この指標が価値を持つのは、まさに集計のまま保てるからです。ルートごとの INP の中央値、ルートごとの LCP の分布、そして個人へ戻る経路はありません。
ソフトナビゲーションは、アプリの最も奥深く、最も遅い部分を初めて計測可能にします。タイミングを集め、それ以外をすべて捨てれば、プロファイルなしでパフォーマンスの全体像が手に入ります。