LCP, INP und CLS korrekt erfassen statt zu raten
Web Vitals in einer Single Page Application zu messen ist keine triviale Angelegenheit, weil klassische Metriken für serverseitig gerenderte Mehrseiten Websites entworfen wurden. Mit der web-vitals Bibliothek, korrektem Timing und echtem Real User Monitoring lassen sich LCP, INP und CLS auch in Vue Apps zuverlässig erfassen.
Inhaltsverzeichnis
- 1. Warum Web Vitals in Vue Apps eine eigene Herausforderung sind
- 2. LCP in Vue Apps verstehen und messen
- 3. INP: Interaktions Latenz in Vue Apps
- 4. CLS: Layout Shifts durch asynchrones Rendering vermeiden
- 5. Die web-vitals Bibliothek integrieren
- 6. Web Vitals an Real User Monitoring senden
- 7. Web Vitals in Nuxt: SSR und Client unterscheiden
- 8. Häufige Messfehler in Single Page Applications
- 9. Web Vitals Metriken im Vergleich
- 10. Zusammenfassung
- 11. FAQ
1. Warum Web Vitals in Vue Apps eine eigene Herausforderung sind
Web Vitals sind die von Google definierten Metriken zur Bewertung der wahrgenommenen Nutzererfahrung einer Website: Largest Contentful Paint für Ladegeschwindigkeit, Interaction to Next Paint für Interaktivität und Cumulative Layout Shift für visuelle Stabilität. Diese Metriken wurden ursprünglich für klassische, serverseitig gerenderte Mehrseiten Websites konzipiert, bei denen jede Navigation einen vollständigen Seitenneuaufbau bedeutet. Eine Vue Single Page Application verhält sich fundamentally anders: Nach dem initialen Laden bleibt die Seite bestehen, Inhalte werden clientseitig ausgetauscht, und der Browser feuert kein neues Navigation Event mehr.
Diese Diskrepanz führt zu einem verbreiteten Missverständnis: Viele Teams messen Web Vitals nur einmal beim initialen Laden der Vue App und ignorieren, dass Nutzer anschließend möglicherweise zehn oder zwanzig Mal innerhalb der Anwendung navigieren, ohne dass ein neuer Seitenladevorgang stattfindet. Die Standard Web Vitals decken diese sogenannten Soft Navigations nicht automatisch ab, was bedeutet, dass ein Performance Problem auf der fünften internen Ansicht komplett unsichtbar bleibt, wenn man sich nur auf die initiale Messung verlässt.
Für Vue Apps braucht Web Vitals Messung deshalb zusätzliche Sorgfalt: Man muss verstehen, welche Metrik sich auf welches Ereignis bezieht, wie man sie mit der offiziellen web-vitals Bibliothek korrekt erfasst, und wie man die Daten an ein Real User Monitoring System sendet, das auch clientseitige Navigationen innerhalb der Vue App abbildet.
2. LCP in Vue Apps verstehen und messen
Largest Contentful Paint misst den Zeitpunkt, an dem das größte sichtbare Element im Viewport gerendert wird, typischerweise ein Hero Bild oder eine große Textüberschrift. In einer Vue App wird dieser Wert vom initialen Laden bis zum ersten vollständigen Rendering gemessen. Problematisch wird es, wenn der LCP Kandidat selbst asynchron nachgeladen wird, etwa ein Produktbild, das erst nach einem API Call gerendert wird. In diesem Fall verzögert sich LCP um die gesamte Dauer des API Calls plus Rendering Zeit, was bei langsamen Backend Antworten schnell zu einem schlechten LCP Wert führt, selbst wenn das JavaScript Bundle selbst klein und schnell ist.
Ein häufiges Muster zur Verbesserung: Kritische LCP Kandidaten Daten bereits während des initialen Server Response oder über einen frühen, parallelen API Call laden, statt sie erst nach der Vue Komponenten Initialisierung anzufordern. Für Nuxt Apps bedeutet das, useAsyncData für den LCP relevanten Inhalt so früh wie möglich im Rendering Baum aufzurufen, statt ihn in einer tief verschachtelten Kindkomponente zu laden, die erst nach mehreren Rendering Zyklen erreicht wird.
// Bad: LCP candidate image waits for a client-side fetch after mount
import { ref, onMounted } from 'vue'
const heroImageUrl = ref(null)
onMounted(async () => {
const res = await fetch('/api/hero-image')
heroImageUrl.value = (await res.json()).url
// LCP is delayed by network round-trip + render
})
3. INP: Interaktions Latenz in Vue Apps
Interaction to Next Paint hat First Input Delay als Interaktivitäts Metrik abgelöst und misst nicht nur die erste Interaktion, sondern die Latenz aller Interaktionen während des gesamten Seitenbesuchs, wobei der schlechteste Wert (mit wenigen Ausnahmen) als repräsentativ gilt. Für Vue Apps ist das besonders relevant, weil eine Single Page Application naturgemäß viele Interaktionen über eine lange Verweildauer hinweg hat, im Gegensatz zu einer klassischen Website mit kurzen Einzelseitenbesuchen.
Ein typischer INP Verschlechterer in Vue Apps: Große, synchron ausgeführte Watcher oder Computed Properties, die bei jeder Nutzereingabe eine teure Neuberechnung auslösen, etwa eine ungefilterte Sortierung einer großen Liste bei jedem Tastendruck in einem Suchfeld. Der Haupt Thread blockiert währenddessen, und der Browser kann das nächste Frame nicht rendern, bis die Berechnung abgeschlossen ist. Debouncing der Eingabe und Verlagerung teurer Berechnungen in requestIdleCallback oder Web Worker sind die gängigen Gegenmaßnahmen für bessere INP Werte.
import { ref, watch } from 'vue'
import { useDebounceFn } from '@vueuse/core'
const searchQuery = ref('')
const filteredResults = ref([])
// Debounced expensive filtering keeps the main thread responsive,
// improving INP by avoiding a heavy synchronous computation per keystroke
const runFilter = useDebounceFn((query) => {
filteredResults.value = allItems.filter(item =>
item.name.toLowerCase().includes(query.toLowerCase())
)
}, 150)
watch(searchQuery, (newQuery) => runFilter(newQuery))
4. CLS: Layout Shifts durch asynchrones Rendering vermeiden
Cumulative Layout Shift misst unerwartete Verschiebungen sichtbarer Elemente. In Vue Apps entstehen Layout Shifts häufig durch asynchron nachgeladene Inhalte, die keinen reservierten Platz im Layout haben: Ein Banner, das nach einem API Call erscheint und den darunterliegenden Inhalt nach unten schiebt, oder eine Liste, deren Höhe sich ändert, sobald die tatsächlichen Daten statt eines Platzhalters gerendert werden. Für eine gute Web Vitals Bewertung muss jeder asynchron geladene Bereich bereits vor dem Laden einen festen oder zumindest geschätzten Platz reservieren.
Skeleton Komponenten mit fester Höhe, die exakt der erwarteten Höhe des finalen Inhalts entsprechen, sind das Standardmuster gegen CLS in Vue Apps. Ebenso wichtig: Bilder ohne explizite width und height Attribute oder ohne aspect-ratio im CSS verursachen Layout Shifts, sobald sie tatsächlich geladen sind, weil der Browser vorher keinen Platz für sie reservieren kann. Dieses Problem betrifft serverseitig gerenderte Inhalte genauso wie clientseitig nachgeladene, ist in Vue Apps aber wegen der häufigen asynchronen Datenladung besonders präsent.
5. Die web-vitals Bibliothek integrieren
Google pflegt die offizielle web-vitals JavaScript Bibliothek, die dieselbe Logik verwendet, die auch Chrome intern und die Chrome User Experience Reports nutzen. Für Vue Apps ist die Integration unkompliziert: Man importiert die benötigten Funktionen und registriert Callbacks, die bei jeder finalisierten Metrik aufgerufen werden. Wichtig ist, diese Integration so früh wie möglich im Anwendungslebenszyklus einzubinden, idealerweise noch vor der Vue App Initialisierung, damit keine frühen Ereignisse verpasst werden.
Die Bibliothek unterscheidet zwischen dem finalen Wert einer Metrik und Zwischenwerten. Für die Web Vitals Erfassung in Vue Apps genügt in der Regel der finale Wert, der über den reportAllChanges Parameter gesteuert wird. Für Debugging Zwecke während der Entwicklung lohnt sich das Loggen aller Zwischenwerte, um zu verstehen, wie sich eine Metrik über die Zeit entwickelt, bevor sie final feststeht.
// main.js — register web-vitals before mounting the Vue app
import { onLCP, onINP, onCLS } from 'web-vitals'
import { createApp } from 'vue'
import App from './App.vue'
function sendToAnalytics(metric) {
const body = JSON.stringify({
name: metric.name,
value: metric.value,
id: metric.id,
rating: metric.rating // 'good' | 'needs-improvement' | 'poor'
})
navigator.sendBeacon('/api/web-vitals', body)
}
onLCP(sendToAnalytics)
onINP(sendToAnalytics)
onCLS(sendToAnalytics)
createApp(App).mount('#app')
6. Web Vitals an Real User Monitoring senden
Synthetische Messungen aus Lighthouse oder PageSpeed Insights zeigen nur eine simulierte Umgebung mit fester Netzwerk und CPU Drosselung. Für belastbare Web Vitals Daten aus echten Nutzersitzungen braucht es Real User Monitoring, das die tatsächlichen Werte über eine große, diverse Nutzerbasis sammelt, inklusive unterschiedlicher Geräte, Netzwerkbedingungen und geografischer Standorte. navigator.sendBeacon ist die bevorzugte Übertragungsmethode, weil sie den Versand auch dann garantiert, wenn der Nutzer die Seite bereits verlässt, was bei einem klassischen fetch Aufruf nicht sichergestellt wäre.
Für Vue Apps mit vielen internen Soft Navigations lohnt sich eine Erweiterung der Standard Integration: Bei jedem Vue Router Navigations Wechsel wird eine neue, separate Web Vitals Messung für die jeweilige Ansicht gestartet, statt sich ausschließlich auf die initiale Messung zu verlassen. So werden Performance Probleme sichtbar, die nur auf bestimmten internen Ansichten auftreten, etwa einer datenintensiven Dashboard Seite, die erst nach mehreren Klicks erreicht wird.
// router/index.js — track Web Vitals per soft navigation
import { onINP, onCLS } from 'web-vitals'
router.afterEach((to) => {
// Reset per-view tracking so INP/CLS reflect this specific view,
// not the entire session since the initial page load
onINP((metric) => sendToAnalytics({ ...metric, route: to.path }), { reportAllChanges: false })
onCLS((metric) => sendToAnalytics({ ...metric, route: to.path }))
})
7. Web Vitals in Nuxt: SSR und Client unterscheiden
In Nuxt Anwendungen mit Server Side Rendering muss zusätzlich zwischen dem serverseitig gerenderten HTML und der clientseitigen Hydration unterschieden werden. LCP wird üblicherweise vom serverseitig gerenderten Inhalt dominiert, sofern dieser tatsächlich sofort sichtbar ist, während INP fast ausschließlich clientseitige Interaktivität nach der Hydration misst. Ein häufiges Problem in Nuxt Apps: Der Inhalt ist zwar serverseitig bereits sichtbar, aber Interaktionen funktionieren erst nach abgeschlossener Hydration, was zu einem sichtbaren, aber nicht interaktiven Zustand führt.
Für die Web Vitals Bewertung in Nuxt ist es wichtig, die web-vitals Integration nur clientseitig auszuführen, da die Bibliothek auf Browser APIs wie PerformanceObserver angewiesen ist, die serverseitig nicht existieren. Ein Nuxt Plugin mit .client.js Suffix stellt sicher, dass die Integration ausschließlich im Browser läuft, während der serverseitige Rendering Prozess unberührt bleibt.
8. Häufige Messfehler in Single Page Applications
Der häufigste Fehler ist, Web Vitals nur einmal beim initialen Laden zu erfassen und interne Navigationen komplett zu ignorieren. Da die Standard Web Vitals API auf das Navigation Timing des Browsers aufbaut, das bei Soft Navigations nicht neu ausgelöst wird, bleiben Performance Probleme auf tiefer liegenden Ansichten unsichtbar, wenn man diese Lücke nicht manuell schließt. Ein zweiter Fehler: Web Vitals Werte aus der lokalen Entwicklungsumgebung mit unrealistisch schnellem Netzwerk und leistungsstarker Hardware werden als repräsentativ für echte Nutzer gehalten, obwohl die tatsächliche Nutzerbasis oft deutlich langsamere Geräte und Verbindungen verwendet.
Ein dritter Fehler betrifft die Interpretation von Aggregatwerten: Der Durchschnitt aller INP Werte verschleiert oft ein bimodales Muster, bei dem die meisten Nutzer eine gute Erfahrung haben, aber eine kleine Gruppe mit alten Geräten oder langsamen Netzwerken massiv schlechtere Werte erzeugt. Perzentile wie das 75. Perzentil, das auch offiziell für die Web Vitals Bewertung verwendet wird, geben ein realistischeres Bild als ein einfacher Mittelwert.
9. Web Vitals Metriken im Vergleich
Die folgende Übersicht ordnet die drei Kern Web Vitals nach dem, was sie messen, und den typischen Ursachen für schlechte Werte in Vue Apps.
| Metrik | Misst | Guter Wert | Typische Ursache in Vue Apps |
|---|---|---|---|
| LCP | Ladezeit des größten Elements | Unter 2,5 Sekunden | LCP Kandidat wartet auf Client Fetch |
| INP | Interaktions Latenz über die Sitzung | Unter 200 Millisekunden | Teure Watcher, ungedebounced Eingaben |
| CLS | Unerwartete Layout Verschiebungen | Unter 0,1 | Kein reservierter Platz für async Inhalt |
Alle drei Metriken hängen zusammen: Eine Optimierung, die LCP verbessert, etwa frühes Laden von Daten, kann CLS verschlechtern, wenn der frühzeitig gerenderte Platzhalter eine andere Höhe hat als der finale Inhalt. Web Vitals in Vue Apps sollten deshalb immer als Gesamtbild betrachtet werden, nicht als isolierte Einzelmetriken, die unabhängig voneinander optimiert werden.
Mironsoft
Web Vitals Monitoring und Performance Optimierung für Vue Apps
Schlechte Web Vitals Werte trotz schneller Entwicklungsumgebung?
Wir integrieren echtes Real User Monitoring in eure Vue App, erfassen LCP, INP und CLS auch bei internen Navigationen und identifizieren die konkreten Ursachen schlechter Werte.
RUM Integration
web-vitals Bibliothek und Anbindung an euer Analytics System
Soft Navigation Tracking
Web Vitals pro interner Ansicht statt nur beim initialen Laden
Konkrete Optimierung
LCP, INP und CLS gezielt verbessern statt pauschal zu raten
10. Zusammenfassung
Web Vitals in Vue Apps zu messen braucht mehr Sorgfalt als bei klassischen Mehrseiten Websites, weil eine Single Page Application nach dem initialen Laden bestehen bleibt und interne Navigationen nicht automatisch als neue Messpunkte erkannt werden. Die offizielle web-vitals Bibliothek liefert die korrekten LCP, INP und CLS Werte, muss aber gezielt mit Vue Router Navigationen verknüpft werden, um auch Probleme auf tiefer liegenden Ansichten sichtbar zu machen.
Real User Monitoring mit navigator.sendBeacon liefert belastbarere Daten als synthetische Lighthouse Messungen, weil es die tatsächliche, diverse Nutzerbasis abbildet. In Nuxt Apps mit Server Side Rendering muss die Integration zusätzlich sauber zwischen serverseitigem Rendering und clientseitiger Hydration unterscheiden. Wer Web Vitals in Vue Apps als Gesamtbild statt isolierter Einzelmetriken betrachtet, erkennt auch Zielkonflikte zwischen den Metriken frühzeitig.
Web Vitals in Vue Apps — Das Wichtigste auf einen Blick
LCP
Kritische Daten früh laden, LCP Kandidaten nicht auf clientseitige Fetches warten lassen.
INP
Teure Watcher und Computed Properties debouncen, Haupt Thread bei Eingaben nicht blockieren.
CLS
Festen Platz für asynchron geladene Inhalte reservieren, Bilder immer mit Dimensionen versehen.
Messung
web-vitals Bibliothek mit sendBeacon und Tracking pro Soft Navigation statt nur beim initialen Laden.