はじめに
React や Vue などのシングルページアプリケーション(SPA)では、ページ遷移のたびにブラウザが実際のナビゲーションを行いません。
JavaScript が URL と表示内容を書き換えるだけなので、ブラウザからすると「ページが切り替わった」と認識されない問題があります。
その結果、Core Web Vitals(LCP・CLS・INP)は最初のページロード時しか正しく計測されず、SPA のパフォーマンス実態が見えにくくなっていました。
この問題を解決するために Chrome チームが取り組んでいるのが ソフトナビゲーション(Soft Navigation) です。
ソフトナビゲーションとは
ソフトナビゲーションは、以下の 3 つの条件をすべて満たす ユーザー操作のことです。
| 条件 | 説明 |
|---|---|
| ユーザーアクション起動 | クリックなど、ユーザーの操作で始まる |
| URL 変更 | ブラウザのアドレスバーに表示される URL が変わる |
| 可視ペイント発生 | インタラクション結果として画面に描画が生じる |
SPA では history.pushState() や location.href の書き換えによって URL が変わりますが、これは従来ブラウザが「ページ遷移」として扱わないものです。
ソフトナビゲーションはこの SPA 的な遷移を、ブラウザが正式に認識・計測できるようにする仕組みです。
なぜ必要なのか
LCP(最大コンテンツ描画)・CLS(累積レイアウトシフト)・INP(次の描画までのインタラクション)などの Core Web Vitals は、ページが切り替わるたびにリセット・再計測されるべきものです。
しかしハードナビゲーション(フルページロード)を前提とした現在の計測では、SPA において次の問題があります。
- 2 ページ目以降の LCP が計測されない
- URL が変わっても CLS・INP がリセットされない
ソフトナビゲーションを検出することで、SPA の各ページ遷移ごとに Core Web Vitals を正確に計測できるようになります。
新しいパフォーマンス API
Chrome はソフトナビゲーションを計測するための新しいエントリタイプを追加しています。
soft-navigation エントリ
const observer = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
console.log(entry.name); // 遷移後の URL
console.log(entry.startTime); // ユーザー操作の開始時刻
console.log(entry.navigationId); // ナビゲーションの一意識別子
console.log(entry.interactionId); // 起点となったインタラクションの識別子
}
});
observer.observe({ type: 'soft-navigation', buffered: true });
interaction-contentful-paint エントリ
ソフトナビゲーション後の LCP を取得するエントリタイプです。
const observer = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
console.log(entry.interactionId); // 対応するインタラクション ID
console.log(entry.largestContentfulPaint); // LCP 値
}
});
observer.observe({ type: 'interaction-contentful-paint', buffered: true });
タイミングの値は、元の「ハード」ナビゲーション開始からの相対値として返されます。
ソフトナビゲーションを基準にした相対時間を求めるには、LCP の時刻からソフトナビゲーションの startTime を差し引く必要があります。
サポートの検出方法
現状は実験的な API のため、利用前にサポートを確認するコードを書くことが推奨されています。
// 推奨: SoftNavigationEntry の存在を確認
if ('SoftNavigationEntry' in window) {
// ソフトナビゲーション対応
}
// 代替: supportedEntryTypes で確認
if (PerformanceObserver.supportedEntryTypes.includes('soft-navigation')) {
// 対応確認
}
Core Web Vitals ごとの計測方法
ソフトナビゲーション時の各指標は、ハードナビゲーションとは少し異なる扱いになります。
| 指標 | ソフトナビゲーション時の扱い |
|---|---|
| TTFB | 0 として報告される |
| FCP |
soft-navigation エントリの presentationTime を基準に計測 |
| LCP |
interaction-contentful-paint エントリから取得 |
| CLS | 各ナビゲーション区間でリセット(0 から再計測) |
| INP | 各ナビゲーション区間でリセット(0 から再計測) |
有効化方法
Chrome フラグで試す
現在は実験的機能のため、フラグで有効化する必要があります。
- Chrome で
chrome://flags/#soft-navigation-heuristicsを開く - Enabled に変更
- Chrome を再起動
コマンドラインで起動する場合
google-chrome --enable-features=SoftNavigationHeuristics
Chrome 151 以降
Chrome 151 からデフォルトで有効化される予定です。
web-vitals ライブラリを使った計測
web-vitals ライブラリの試験運用版ブランチ soft-navs でソフトナビゲーション対応の計測がサポートされています。
import { onLCP, onCLS, onINP } from 'web-vitals/soft-navs';
// ソフトナビゲーション対応の LCP を計測
onLCP(console.log, { reportSoftNavs: true });
// CLS・INP も同様
onCLS(console.log, { reportSoftNavs: true });
onINP(console.log, { reportSoftNavs: true });
このアプローチを使うと、コールバックに navigationId と navigationURL が自動付与されるため、どのページ遷移に対応する計測値かを特定できます。
DevTools でのサポート
Chrome DevTools のパフォーマンスパネルでは、ソフトナビゲーションが * マーク付きで表示されます。
LCP マーカーも各ソフトナビゲーションごとに表示されるため、視覚的に確認しやすくなっています。
詳細指標のライブビューなど、一部の機能は後日追加予定です。現時点では計測結果の完全なサポートではありません。
実装上の注意点
interactionId で LCP をマッピングする
interaction-contentful-paint エントリを対応するナビゲーションに結びつける際は、navigationId ではなく interactionId を使う必要があります。
理由は次の 2 点です。
- URL 更新前にペイントが発生した場合、古い
navigationIdが含まれる可能性がある - 後続インタラクションによって LCP 対象外のペイントも発生する
アニメーション背景に注意
アニメーション背景はソフトナビゲーション時の LCP 対象外ですが、ハードナビゲーション時は対象となりえます。
同じコンテンツでも読み込み方法によって LCP の対象要素が変わるため注意してください。
ブラウザサポート
| ブラウザ | サポート |
|---|---|
| Chrome(Chromium 系) | 対応中(フラグ必要、Chrome 151 でデフォルト有効化予定) |
| Firefox | 未対応 |
| Safari | 未対応 |
現時点では Chromium 系のみの対応です。
RUM(リアルユーザーモニタリング)ツールを利用している場合は、プロバイダのサポート状況も合わせて確認してください。
まとめ
ソフトナビゲーションは、SPA が普及した現在の Web においてページ遷移ごとのパフォーマンスを正確に計測するための重要な仕組みです。
- SPA のルーティングを「ナビゲーション」として認識させる
- LCP・CLS・INP を遷移ごとにリセット・再計測できる
-
soft-navigation/interaction-contentful-paintエントリで計測可能 -
web-vitalsライブラリのsoft-navsブランチで手軽に試せる - Chrome 151 からデフォルト有効化予定
SPA を扱うフロントエンドエンジニアにとって、ユーザー体験の実態をより正確に把握するための強力なツールになりそうです。まずは Chrome フラグを有効化してデベロッパーツールで確認してみてください。
参考
最後まで読んでくださってありがとうございます!
普段はデザインやフロントエンドを中心にQiitaに記事を投稿しているので、ぜひQiitaのフォローとX(Twitter)のフォローをお願いします。