Start · Sprachen · JavaScript · Referenz · PerformanceObserver

PerformanceObserver

Schnittstelle

Beobachtet die Performance-Zeitleiste und ruft einen Callback auf, sobald neue Performance-Einträge (z. B. <code>resource</code>, <code>paint</code>, <code>longtask</code>) erfasst werden.

seit JavaScript Baseline: alle modernen Browser (seit 2017); Node. Kategorie: webapi

Signatur

interface PerformanceObserver

Beschreibung

Mit PerformanceObserver reagierst du ereignisgesteuert auf neue Leistungsmessungen, statt die Zeitleiste regelmäßig über performance.getEntries() abzufragen. Der Browser sammelt Einträge wie Ressourcen-Ladezeiten, Navigations-Timings, eigene Marken (mark/measure), Paint-Zeitpunkte, Long Tasks oder Layout-Verschiebungen und übergibt sie gebündelt an deinen Callback.

Typische Einsatzgebiete sind Real User Monitoring (RUM) und die Erfassung der Core Web Vitals (LCP, CLS, INP) direkt im Browser der Nutzer. Der Observer verursacht kaum Overhead, da die Callbacks asynchron und mit niedriger Priorität ausgeführt werden und die Hauptthread-Arbeit nicht blockieren.

Die Schnittstelle besteht aus dem Konstruktor new PerformanceObserver(callback), den Instanzmethoden observe(), disconnect() und takeRecords() sowie der statischen Eigenschaft PerformanceObserver.supportedEntryTypes, mit der du vorab prüfen kannst, welche Eintragstypen der Browser unterstützt.

Mit der Option buffered: true erhältst du auch Einträge, die bereits vor dem Anlegen des Observers entstanden sind – wichtig, weil Analyse-Skripte oft erst spät geladen werden.

Parameter

Name Typ Default Beschreibung
$callback Pflicht (list: PerformanceObserverEntryList, observer: PerformanceObserver, options?: { droppedEntriesCount: number }) => void Konstruktor-Argument. Wird aufgerufen, wenn neue Einträge der beobachteten Typen vorliegen. list.getEntries(), getEntriesByType() und getEntriesByName() liefern die Einträge.
$options.type string Für observe(): ein einzelner Eintragstyp, z. B. 'largest-contentful-paint'. Nur mit type sind buffered und durationThreshold nutzbar.
$options.entryTypes string[] Für observe(): mehrere Eintragstypen auf einmal. Darf nicht mit type kombiniert werden.
$options.buffered boolean false Liefert zusätzlich bereits gepufferte, ältere Einträge des Typs.
$options.durationThreshold number 104 Nur für type: 'event': Mindestdauer in Millisekunden (min. 16), ab der Event-Timing-Einträge gemeldet werden.

Rückgabewert

Typ
PerformanceObserver
Beschreibung
Der Konstruktor liefert eine neue Observer-Instanz. observe() und disconnect() geben undefined zurück, takeRecords() ein Array von PerformanceEntry-Objekten.

Beispiele

Ressourcen-Ladezeiten protokollieren

const observer = new PerformanceObserver((list) => {
  for (const entry of list.getEntries()) {
    console.log(`${entry.name}: ${entry.duration.toFixed(1)} ms`);
  }
});

// Auch bereits geladene Ressourcen berücksichtigen
observer.observe({ type: 'resource', buffered: true });
https://example.com/app.js: 84.3 ms https://example.com/style.css: 41.7 ms

Largest Contentful Paint (LCP) messen

if (PerformanceObserver.supportedEntryTypes.includes('largest-contentful-paint')) {
  let lcp = 0;
  const po = new PerformanceObserver((list) => {
    const entries = list.getEntries();
    lcp = entries.at(-1).startTime; // letzter Kandidat zählt
  });
  po.observe({ type: 'largest-contentful-paint', buffered: true });

  // Beim Verlassen der Seite Ergebnis senden
  document.addEventListener('visibilitychange', () => {
    if (document.visibilityState === 'hidden') {
      po.takeRecords();
      po.disconnect();
      navigator.sendBeacon('/rum', JSON.stringify({ lcp }));
    }
  }, { once: true });
}

Eigene Messungen mit mark/measure beobachten

const po = new PerformanceObserver((list, observer) => {
  list.getEntriesByType('measure').forEach((m) => {
    console.log(`${m.name} dauerte ${Math.round(m.duration)} ms`);
  });
  observer.disconnect(); // nach erster Meldung beenden
});
po.observe({ entryTypes: ['measure'] });

const loadUsers = async () => {
  performance.mark('users-start');
  await fetch('https://jsonplaceholder.typicode.com/users');
  performance.mark('users-end');
  performance.measure('users-laden', 'users-start', 'users-end');
};
loadUsers();
users-laden dauerte 212 ms

Long Tasks erkennen

const longTasks = new PerformanceObserver((list) => {
  for (const task of list.getEntries()) {
    console.warn(`Long Task: ${task.duration} ms ab ${Math.round(task.startTime)} ms`);
  }
});
longTasks.observe({ type: 'longtask', buffered: true });
Long Task: 137 ms ab 1840 ms

// Wichtig · Fallstricke

  • type und entryTypes nicht mischen: Beides in einem observe()-Aufruf wirft einen TypeError. buffered wirkt nur zusammen mit type.
  • Unterstützung variiert je Eintragstyp: longtask, largest-contentful-paint, layout-shift und event sind vor allem in Chromium-Browsern verfügbar; Safari und Firefox unterstützen nur einen Teil. Immer mit PerformanceObserver.supportedEntryTypes prüfen – unbekannte Typen werden stillschweigend ignoriert (Konsolenwarnung).
  • Asynchrone Zustellung: Der Callback läuft verzögert (Idle-Priorität). Vor dem Verlassen der Seite mit takeRecords() noch ausstehende Einträge abholen.
  • Puffergrenzen: Der Ressourcen-Puffer fasst standardmäßig 250 Einträge (performance.setResourceTimingBufferSize()); verworfene Einträge meldet droppedEntriesCount im dritten Callback-Argument.
  • Datenschutz/CORS: Bei Cross-Origin-Ressourcen sind detaillierte Timings (z. B. transferSize, DNS-Zeiten) nur mit dem Response-Header Timing-Allow-Origin verfügbar, sonst 0.
  • Nicht mehr benötigte Observer mit disconnect() beenden, um Speicher freizugeben.