Signatur
Beschreibung
Der Konstruktor PerformanceObserver() erzeugt einen Beobachter für die Performance Timeline. Anders als beim Abfragen mit performance.getEntriesByType() muss man nicht regelmäßig nachsehen: Der Browser ruft den Callback asynchron auf, sobald neue Einträge wie resource, navigation, mark, measure, paint, largest-contentful-paint, layout-shift, longtask oder event vorliegen.
Direkt nach dem Erzeugen beobachtet das Objekt noch nichts. Erst ein Aufruf von observer.observe() legt fest, welche Eintragstypen gemeldet werden, entweder mit { type: '…', buffered: true } für einen einzelnen Typ oder mit { entryTypes: [...] } für mehrere.
Typische Einsatzgebiete sind Real User Monitoring (RUM), das Messen der Core Web Vitals (LCP, CLS, INP), das Erkennen langer Tasks, die den Haupt-Thread blockieren, und das Auswerten eigener Messpunkte, die mit performance.mark() und performance.measure() gesetzt wurden.
Parameter
| Name | Typ | Default | Beschreibung |
|---|---|---|---|
| $callback Pflicht | (entries: PerformanceObserverEntryList, observer: PerformanceObserver, options?: { droppedEntriesCount: number }) => void | Wird aufgerufen, sobald beobachtete Einträge erfasst werden. Das erste Argument ist eine PerformanceObserverEntryList (mit getEntries(), getEntriesByType() und getEntriesByName()), das zweite der Beobachter selbst. Das dritte Argument enthält droppedEntriesCount, also die Zahl der Einträge, die verloren gingen, weil der Puffer voll war (nur bei buffered: true). |
Rückgabewert
PerformanceObserver-Objekt, das erst nach einem Aufruf von observe() Einträge meldet.Beispiele
Ressourcen-Ladezeiten protokollieren
const observer = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
console.log(`${entry.name}: ${entry.duration.toFixed(1)} ms`);
}
});
// buffered: true liefert auch Einträge, die vor observe() entstanden sind
observer.observe({ type: 'resource', buffered: true });
Largest Contentful Paint (LCP) messen
let lcp = 0;
const lcpObserver = new PerformanceObserver((list) => {
const entries = list.getEntries();
// Der letzte Eintrag ist der aktuelle LCP-Kandidat
lcp = entries.at(-1).startTime;
});
lcpObserver.observe({ type: 'largest-contentful-paint', buffered: true });
// Beim Verlassen der Seite den Wert senden
addEventListener('visibilitychange', () => {
if (document.visibilityState === 'hidden') {
lcpObserver.takeRecords();
lcpObserver.disconnect();
navigator.sendBeacon('/analytics', JSON.stringify({ lcp }));
}
}, { once: true });
Eigene Messpunkte mit mark/measure auswerten
const observer = new PerformanceObserver((list, obs) => {
for (const m of list.getEntriesByType('measure')) {
console.log(`${m.name} dauerte ${m.duration.toFixed(2)} ms`);
}
obs.disconnect(); // nur einmal auswerten
});
observer.observe({ entryTypes: ['measure'] });
performance.mark('sort-start');
const data = Array.from({ length: 100_000 }, () => Math.random()).sort();
performance.mark('sort-end');
performance.measure('sortieren', 'sort-start', 'sort-end');
Lange Tasks erkennen (mit Feature-Prüfung)
const supported = PerformanceObserver.supportedEntryTypes ?? [];
if (supported.includes('longtask')) {
new PerformanceObserver((list) => {
for (const task of list.getEntries()) {
console.warn(`Langer Task: ${Math.round(task.duration)} ms`);
}
}).observe({ type: 'longtask', buffered: true });
} else {
console.info('longtask wird hier nicht unterstützt');
}
// Wichtig · Fallstricke
- Ohne
newgeht es nicht: Ein Aufruf als normale Funktion wirft einenTypeError. Das gilt auch, wenncallbackfehlt oder keine Funktion ist. typeundentryTypesnicht mischen: Inobserve()schließen sich beide Optionen gegenseitig aus.bufferedunddurationThresholdfunktionieren nur zusammen mittype.- Die Unterstützung ist je nach Eintragstyp verschieden:
longtask,largest-contentful-paintundlayout-shiftgibt es derzeit vor allem in Chromium-Browsern, Safari und Firefox unterstützen sie nur teilweise oder gar nicht. Vorher mitPerformanceObserver.supportedEntryTypesprüfen. Nicht unterstützte Typen werden stillschweigend ignoriert, meist nur mit einer Warnung in der Konsole. - Der Callback läuft asynchron (bei Leerlauf oder gebündelt). Deshalb vor dem Verlassen der Seite noch ausstehende Einträge mit
takeRecords()abholen. - Performance: Im Callback keine aufwendigen Arbeiten und keine DOM-Änderungen durchführen, sonst verfälscht man die Messung selbst. Nicht mehr benötigte Beobachter mit
disconnect()beenden. - Datenschutz: Bei Ressourcen von fremden Ursprüngen sind die meisten Zeitwerte 0, solange der Server keinen
Timing-Allow-Origin-Header sendet. Zeitstempel sind außerdem absichtlich ungenauer gemacht (Schutz vor Spectre-Angriffen). - In Node.js steht die Klasse über
require('node:perf_hooks')zur Verfügung, ab Version 16 auch global. Unterstützt werden dort andere Eintragstypen (z. B.gc,function,http).