Signatur
Beschreibung
In einer klassischen Mehrseiten-Anwendung erzeugt jeder Seitenwechsel eine neue Dokument-Navigation, die über PerformanceNavigationTiming sowie Metriken wie LCP gemessen werden kann. Single-Page-Applications (React Router, Vue Router, Angular usw.) wechseln die Ansicht dagegen per JavaScript, ohne ein neues Dokument zu laden. Solche Soft-Navigationen waren für die Performance-APIs bisher nicht sichtbar. Deshalb galten Core Web Vitals in SPAs oft nur für den ersten Seitenaufruf.
PerformanceSoftNavigation schließt diese Lücke. Der Browser erkennt eine Soft-Navigation mithilfe einer Heuristik und erzeugt dann einen Performance-Eintrag mit dem entryType "soft-navigation". Die Heuristik besteht aus drei Bedingungen:
- Der Nutzer löst eine Interaktion aus (z. B.
clickoderkeydown). - Die URL ändert sich per History-API (
history.pushState()/replaceState()) oder Navigation-API. - Das DOM wird sichtbar verändert und anschließend gezeichnet.
Die wichtigsten Eigenschaften (größtenteils von PerformanceEntry geerbt):
name: die neue URL nach der Soft-Navigation.entryType: immer"soft-navigation".startTime: Zeitstempel der auslösenden Nutzerinteraktion (in Millisekunden, bezogen aufperformance.timeOrigin).duration: Zeit von der Interaktion bis zum Abschluss der Navigation bzw. bis zum ersten relevanten Paint.navigationId: eindeutige ID der Navigation. Andere Einträge wie LCP, Layout-Shift oder Event-Timing tragen dieselbe ID. So lassen sie sich einer bestimmten „virtuellen Seite“ zuordnen.
Weitere Felder wie interactionId oder Paint-Zeitstempel (paintTime, presentationTime) werden je nach Spezifikationsstand in Chromium ergänzt. Die Einträge liest man typischerweise mit einem PerformanceObserver aus und schickt sie an ein RUM-System (Real User Monitoring), um Ladezeiten pro Route zu messen.
Rückgabewert
PerformanceObserver oder performance.getEntriesByType("soft-navigation").Beispiele
Soft-Navigationen mit PerformanceObserver beobachten
// Prüfen, ob der Browser den Eintragstyp unterstützt
if (PerformanceObserver.supportedEntryTypes.includes('soft-navigation')) {
const observer = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
console.log(
`Soft-Navigation zu ${entry.name}: ` +
`Start ${entry.startTime.toFixed(0)} ms, ` +
`Dauer ${entry.duration.toFixed(0)} ms, ID ${entry.navigationId}`
);
}
});
// buffered: true liefert auch bereits aufgetretene Einträge
observer.observe({ type: 'soft-navigation', buffered: true });
} else {
console.log('Soft-Navigation-Messung wird nicht unterstützt.');
}
Soft-Navigation im SPA-Router auslösen
// Minimal-Router: Klick + pushState + DOM-Änderung erfüllen die Heuristik
document.querySelector('#link-kontakt').addEventListener('click', (event) => {
event.preventDefault();
history.pushState({}, '', '/kontakt');
document.querySelector('main').innerHTML = '<h1>Kontakt</h1><p>Schreib uns!</p>';
});
// Später alle erfassten Soft-Navigationen abfragen
setTimeout(() => {
const entries = performance.getEntriesByType('soft-navigation');
console.log(entries.map((e) => e.name));
}, 2000);
LCP-Einträge einer Soft-Navigation zuordnen (RUM)
const routes = new Map(); // navigationId → { url, start, lcp }
new PerformanceObserver((list) => {
for (const nav of list.getEntries()) {
routes.set(nav.navigationId, { url: nav.name, start: nav.startTime, lcp: null });
}
}).observe({ type: 'soft-navigation', buffered: true });
new PerformanceObserver((list) => {
for (const lcp of list.getEntries()) {
const route = routes.get(lcp.navigationId);
if (route) {
// LCP relativ zum Beginn der Soft-Navigation
route.lcp = lcp.startTime - route.start;
}
}
}).observe({ type: 'largest-contentful-paint', buffered: true, includeSoftNavigationObservations: true });
// Beim Verlassen der Seite an den Server senden
addEventListener('visibilitychange', () => {
if (document.visibilityState === 'hidden') {
navigator.sendBeacon('/rum', JSON.stringify([...routes.values()]));
}
});
// Wichtig · Fallstricke
Experimentell: Die API gibt es bislang nur in Chromium-basierten Browsern, und dort nur mit aktiviertem Flag (chrome://flags/#enable-experimental-web-platform-features bzw. „Soft Navigation Heuristics“) oder über einen Origin Trial. Firefox und Safari unterstützen sie nicht. Prüfe die Unterstützung immer mit PerformanceObserver.supportedEntryTypes.includes('soft-navigation').
- Die Spezifikation (WICG „Soft Navigations“) ändert sich noch. Eigenschaftsnamen und Observer-Optionen wie
includeSoftNavigationObservationskönnen sich ändern oder wegfallen. - Die Erkennung beruht auf einer Heuristik. Programmatische URL-Wechsel ohne Nutzerinteraktion (z. B. Weiterleitungen per Timer) oder Wechsel ohne sichtbare DOM-Änderung werden nicht als Soft-Navigation erkannt.
- Die Werte von
startTimebeziehen sich weiterhin auf den Ursprung des Dokuments (performance.timeOrigin). Für routenbezogene Metriken musst du denstartTimeder Soft-Navigation selbst abziehen. - Ohne
buffered: trueentgehen dir Einträge, die vor der Registrierung des Observers entstanden sind.