Benutzerdefinierte Messwerte

Nutzerorientierte Messwerte, die sich universell auf jeder Website messen lassen, sind sehr wertvoll. Mit diesen Messwerten können Sie:

  • Verstehen, wie echte Nutzer das Web insgesamt erleben
  • Vergleichen Sie Ihre Website mit der eines Mitbewerbers.
  • Sie können nützliche und umsetzbare Daten in Ihren Analysetools erfassen, ohne benutzerdefinierten Code schreiben zu müssen.

Universelle Messwerte bieten eine gute Ausgangsbasis. In vielen Fällen müssen Sie jedoch mehr als nur diese Messwerte erfassen, um die gesamte Nutzererfahrung Ihrer Website zu erfassen.

Mit benutzerdefinierten Messwerten können Sie Aspekte der Nutzerfreundlichkeit Ihrer Website messen, die möglicherweise nur für Ihre Website gelten, z. B.:

  • Die Zeit, die es dauert, bis eine Single-Page-Anwendung (SPA) von einer Seite zur anderen wechselt.
  • Die Zeit, die vergeht, bis auf einer Seite Daten aus einer Datenbank für angemeldete Nutzer angezeigt werden.
  • Wie lange dauert es, bis eine serverseitig gerenderte App (SSR) hydratisiert ist?
  • Die Cache-Trefferquote für Ressourcen, die von wiederkehrenden Besuchern geladen werden.
  • Die Ereignislatenz von Klick- oder Tastaturereignissen in einem Spiel.

APIs zum Messen benutzerdefinierter Messwerte

Bisher hatten Webentwickler nicht viele Low-Level-APIs zur Leistungsmessung. Daher mussten sie auf Hacks zurückgreifen, um zu messen, ob eine Website eine gute Leistung erbringt.

So können Sie beispielsweise feststellen, ob der Haupt-Thread aufgrund lang laufender JavaScript-Aufgaben blockiert ist, indem Sie eine requestAnimationFrame-Schleife ausführen und das Delta zwischen den einzelnen Frames berechnen. Wenn das Delta deutlich länger als die Framerate des Displays ist, können Sie dies als lange Aufgabe melden. Solche Hackerangriffe werden jedoch nicht empfohlen, da sie sich in Wirklichkeit selbst auf die Leistung auswirken, z. B. indem sie den Akku entladen.

Die erste Regel für eine effektive Leistungsmessung besteht darin, sicherzustellen, dass Ihre Techniken zur Leistungsmessung selbst keine Leistungsprobleme verursachen. Verwenden Sie daher nach Möglichkeit eine der folgenden APIs für alle benutzerdefinierten Messwerte, die Sie auf Ihrer Website erfassen.

Performance Observer API

Unterstützte Browser

  • Chrome: 52.
  • Edge: 79.
  • Firefox: 57.
  • Safari: 11.

Quelle

Mit der Performance Observer API werden Daten aus allen anderen auf dieser Seite beschriebenen Leistungs-APIs erfasst und angezeigt. Um gute Daten zu erhalten, ist es wichtig, dies zu verstehen.

Mit PerformanceObserver können Sie Leistungsbezogene Ereignisse passiv abonnieren. So können API-Callbacks während Inaktivitätszeiten ausgelöst werden, was bedeutet, dass sie in der Regel die Seitenleistung nicht beeinträchtigen.

Wenn Sie eine PerformanceObserver erstellen möchten, übergeben Sie einen Callback, der ausgeführt werden soll, wenn neue Leistungseinträge gesendet werden. Dann geben Sie mit der Methode observe() an, nach welchen Arten von Einträgen der Beobachter suchen soll:

const po = new PerformanceObserver((list) => {
  for (const entry of list.getEntries()) {
    // Log the entry and all associated details.
    console.log(entry.toJSON());
  }
});

po.observe({type: 'some-entry-type'});

In den folgenden Abschnitten werden alle verschiedenen Eintragstypen aufgeführt, die beobachtet werden können. In neueren Browsern können Sie jedoch über die statische Eigenschaft PerformanceObserver.supportedEntryTypes prüfen, welche Eintragstypen verfügbar sind.

Einträge beobachten, die bereits stattgefunden haben

Standardmäßig können PerformanceObserver-Objekte Einträge nur bei Auftreten beobachten. Dies kann zu Problemen führen, wenn Sie den Code für die Leistungsanalyse per Lazy Loading laden möchten, damit Ressourcen mit höherer Priorität nicht blockiert werden.

Wenn Sie bisherige Einträge abrufen möchten (nachdem sie aufgetreten sind), setzen Sie das Flag buffered auf true, wenn Sie observe() aufrufen. Beim ersten Aufruf des PerformanceObserver-Callbacks fügt der Browser bisherige Einträge aus seinem Leistungseintrags-Puffer hinzu, bis die maximale Puffergröße für diesen Typ erreicht ist.

po.observe({
  type: 'some-entry-type',
  buffered: true,
});

Zu vermeidende Legacy-Leistungs-APIs

Vor der Performance Observer API konnten Entwickler mit den folgenden drei Methoden, die für das performance-Objekt definiert sind, auf Leistungseinträge zugreifen:

Obwohl diese APIs weiterhin unterstützt werden, wird ihre Verwendung nicht empfohlen, da sie nicht zulassen, dass Sie darauf warten, wenn neue Einträge ausgegeben werden. Außerdem werden viele neue APIs (z. B. largest-contentful-paint) nicht über das performance-Objekt, sondern nur über PerformanceObserver bereitgestellt.

Sofern Sie keine spezielle Kompatibilität mit dem Internet Explorer benötigen, sollten Sie diese Methoden in Ihrem Code vermeiden und stattdessen PerformanceObserver verwenden.

User Timing API

Unterstützte Browser

  • Chrome: 28.
  • Rand: 12.
  • Firefox: 38.
  • Safari: 11.

Quelle

Die User Timing API ist eine allgemeine Analyse-API für zeitbasierte Messwerte. Sie können beliebige Zeitpunkte markieren und die Dauer zwischen diesen Markierungen später messen.

// Record the time immediately before running a task.
performance.mark('myTask:start');
await doMyTask();

// Record the time immediately after running a task.
performance.mark('myTask:end');

// Measure the delta between the start and end of the task
performance.measure('myTask', 'myTask:start', 'myTask:end');

APIs wie Date.now() oder performance.now() bieten ähnliche Funktionen. Der Vorteil der User Timing API besteht darin, dass sie sich gut in Leistungstools integrieren lässt. In Chrome DevTools werden beispielsweise Nutzerzeitmessungen im Bereich „Leistung“ visualisiert. Viele Analyseanbieter erfassen auch automatisch alle von Ihnen durchgeführten Messungen und senden die Daten zur Dauer an ihr Analyse-Backend.

Wenn Sie Messungen zu Nutzerzeiträumen erfassen möchten, können Sie PerformanceObserver verwenden und sich für die Beobachtung von Einträgen vom Typ measure registrieren:

// Create the performance observer.
const po = new PerformanceObserver((list) => {
  for (const entry of list.getEntries()) {
    // Log the entry and all associated details.
    console.log(entry.toJSON());
  }
});

// Start listening for `measure` entries to be dispatched.
po.observe({type: 'measure', buffered: true});

Long Tasks API

Unterstützte Browser

  • Chrome: 58.
  • Edge: 79.
  • Firefox: nicht unterstützt
  • Safari: Nicht unterstützt.

Quelle

Mit der Long Tasks API können Sie feststellen, wann der Hauptthread des Browsers so lange blockiert ist, dass sich das auf die Framerate oder die Eingabelatenz auswirkt. Die API meldet alle Aufgaben, die länger als 50 Millisekunden ausgeführt werden.

Wenn Sie Code mit hoher Auslastung ausführen oder große Scripts laden und ausführen müssen, ist es nützlich zu verfolgen, ob dieser Code den Haupt-Thread blockiert. Viele Messwerte der höheren Ebene basieren sogar auf der Long Tasks API selbst, z. B. Time to Interactive (TTI) und Total Blocking Time (TBT).

Wenn Sie feststellen möchten, wann lange Aufgaben ausgeführt werden, können Sie PerformanceObserver verwenden und sich für die Beobachtung von Einträgen vom Typ longtask registrieren:

// Create the performance observer.
const po = new PerformanceObserver((list) => {
  for (const entry of list.getEntries()) {
    // Log the entry and all associated details.
    console.log(entry.toJSON());
  }
});

// Start listening for `longtask` entries to be dispatched.
po.observe({type: 'longtask', buffered: true});

Long Animation Frames API

Unterstützte Browser

  • Chrome: 123.
  • Edge: 123.
  • Firefox: Nicht unterstützt.
  • Safari: wird nicht unterstützt.

Quelle

Die Long Animation Frames API ist eine neue Version der Long Tasks API, die lange Frames (nicht lange Aufgaben) von über 50 Millisekunden betrachtet. Damit werden einige Mängel der Long Tasks API beseitigt, einschließlich einer besseren Attribution und der größeren Anzahl potenziell problematischer Verzögerungen.

Um zu ermitteln, wann lange Frames passieren, können Sie PerformanceObserver verwenden und sich registrieren, um Einträge vom Typ long-animation-frame zu beobachten:

// Create the performance observer.
const po = new PerformanceObserver((list) => {
  for (const entry of list.getEntries()) {
    // Log the entry and all associated details.
    console.log(entry.toJSON());
  }
});

// Start listening for `long-animation-frame` entries to be dispatched.
po.observe({type: 'long-animation-frame', buffered: true});

Element Timing API

Unterstützte Browser

  • Chrome: 77.
  • Edge: 79.
  • Firefox: Nicht unterstützt.
  • Safari: Nicht unterstützt.

Quelle

Der Messwert Largest Contentful Paint (LCP) gibt Aufschluss darüber, wann das größte Bild oder der größte Textblock auf dem Bildschirm dargestellt wurde. In einigen Fällen möchten Sie jedoch die Renderingzeit eines anderen Elements messen.

Verwenden Sie in diesen Fällen die Element Timing API. Die LCP API basiert auf der Element Timing API und ermöglicht automatische Berichte zum größten Element mit Inhalt. Sie können aber auch Berichte zu anderen Elementen erstellen, indem Sie ihnen explizit das elementtiming-Attribut hinzufügen und einen PerformanceObserver registrieren, um den element-Eintragstyp zu beobachten.

<img elementtiming="hero-image" />
<p elementtiming="important-paragraph">This is text I care about.</p>
<!-- ... -->

<script>
  const po = new PerformanceObserver((entryList) => {
    for (const entry of entryList.getEntries()) {
      // Log the entry and all associated details.
      console.log(entry.toJSON());
    }
  });

  // Start listening for `element` entries to be dispatched.
  po.observe({type: 'element', buffered: true});
</script>

Event Timing API

Unterstützte Browser

  • Chrome: 76.
  • Edge: 79.
  • Firefox: 89.
  • Safari: Nicht unterstützt.

Quelle

Der Messwert Interaction to Next Paint (INP) bewertet die Reaktionsfähigkeit der Seite insgesamt, indem alle Klicks, Berührungen und Tastaturinteraktionen während der Lebensdauer einer Seite beobachtet werden. Der INP einer Seite ist in den meisten Fällen die Interaktion, die am längsten gedauert hat, vom Zeitpunkt, an dem der Nutzer die Interaktion gestartet hat, bis zum Zeitpunkt, an dem der Browser den nächsten Frame anzeigt, der das visuelle Ergebnis der Nutzereingabe zeigt.

Der Messwert „In-Page-Nutzer“ wird durch die Event Timing API ermöglicht. Diese API stellt eine Reihe von Zeitstempeln bereit, die während des Lebenszyklus des Ereignisses auftreten, darunter:

  • startTime: der Zeitpunkt, zu dem der Browser das Ereignis empfängt.
  • processingStart: der Zeitpunkt, zu dem der Browser mit der Verarbeitung von Ereignishandlern für das Ereignis beginnen kann.
  • processingEnd: Zeitpunkt, zu dem der Browser die Ausführung aller synchronen Codes beendet, die von Ereignishandlern für dieses Ereignis initiiert wurden.
  • duration: Die Zeit (aus Sicherheitsgründen auf 8 Millisekunden gerundet), die vergeht, bis der Browser das Ereignis empfängt und den nächsten Frame zeichnen kann, nachdem der gesamte synchrone Code ausgeführt wurde, der von den Ereignis-Handlern initiiert wurde.

Das folgende Beispiel zeigt, wie diese Werte zum Erstellen benutzerdefinierter Messungen verwendet werden:

const po = new PerformanceObserver((entryList) => {
  // Get the last interaction observed:
  const entries = Array.from(entryList.getEntries()).forEach((entry) => {
    // Get various bits of interaction data:
    const inputDelay = entry.processingStart - entry.startTime;
    const processingTime = entry.processingEnd - entry.processingStart;
    const presentationDelay = entry.startTime + entry.duration - entry.processingEnd;
    const duration = entry.duration;
    const eventType = entry.name;
    const target = entry.target || "(not set)"

    console.log("----- INTERACTION -----");
    console.log(`Input delay (ms): ${inputDelay}`);
    console.log(`Event handler processing time (ms): ${processingTime}`);
    console.log(`Presentation delay (ms): ${presentationDelay}`);
    console.log(`Total event duration (ms): ${duration}`);
    console.log(`Event type: ${eventType}`);
    console.log(target);
  });
});

// A durationThreshold of 16ms is necessary to include more
// interactions, since the default is 104ms. The minimum
// durationThreshold is 16ms.
po.observe({type: 'event', buffered: true, durationThreshold: 16});

Resource Timing API

Unterstützte Browser

  • Chrome: 29.
  • Edge: 12.
  • Firefox: 35.
  • Safari: 11.

Quelle

Die Resource Timing API bietet Entwicklern detaillierte Informationen dazu, wie Ressourcen für eine bestimmte Seite geladen wurden. Trotz des Namens der API sind die bereitgestellten Informationen nicht nur auf Zeitdaten beschränkt (obwohl es davon viele gibt). Weitere Daten, auf die Sie zugreifen können:

  • initiatorType: Gibt an, wie die Ressource abgerufen wurde, z. B. über ein <script>- oder <link>-Tag oder über einen fetch()-Aufruf.
  • nextHopProtocol: das Protokoll, das zum Abrufen der Ressource verwendet wird, z. B. h2 oder quic.
  • encodedBodySize/decodedBodySize: Die Größe der Ressource in ihrer codierten oder decodierten Form.
  • transferSize: Die Größe der Ressource, die tatsächlich über das Netzwerk übertragen wurde. Wenn Ressourcen aus dem Cache bereitgestellt werden, kann dieser Wert viel kleiner als encodedBodySize sein und in einigen Fällen sogar null (wenn keine Cache-Neuvalidierung erforderlich ist).

Mit der transferSize-Eigenschaft von Ressourcenzeiteinträgen können Sie den Messwert Cache-Trefferquote oder den Messwert Größe der im Cache gespeicherten Ressourcen messen. So können Sie nachvollziehen, wie sich Ihre Ressourcen-Caching-Strategie auf die Leistung bei wiederkehrenden Besuchern auswirkt.

Im folgenden Beispiel werden alle von der Seite angeforderten Ressourcen protokolliert und es wird angegeben, ob die jeweilige Ressource aus dem Cache stammt oder nicht.

// Create the performance observer.
const po = new PerformanceObserver((list) => {
  for (const entry of list.getEntries()) {
    // If transferSize is 0, the resource was fulfilled using the cache.
    console.log(entry.name, entry.transferSize === 0);
  }
});

// Start listening for `resource` entries to be dispatched.
po.observe({type: 'resource', buffered: true});

Unterstützte Browser

  • Chrome: 57.
  • Edge: 12.
  • Firefox: 58.
  • Safari: 15.

Quelle

Die Navigation Timing API ähnelt der Resource Timing API, meldet jedoch nur Navigationsanfragen. Der navigation-Eintragstyp ähnelt auch dem resource-Eintragstyp, enthält aber einige zusätzliche Informationen, die nur für Navigationsanfragen gelten (z. B. wenn die Ereignisse DOMContentLoaded und load ausgelöst werden).

Ein Messwert, den viele Entwickler zur Analyse der Serverantwortzeit (Time to First Byte (TTFB)) verwenden, ist über die Navigation Timing API verfügbar. Dabei handelt es sich um den responseStart-Zeitstempel des Eintrags.

// Create the performance observer.
const po = new PerformanceObserver((list) => {
  for (const entry of list.getEntries()) {
    // If transferSize is 0, the resource was fulfilled using the cache.
    console.log('Time to first byte', entry.responseStart);
  }
});

// Start listening for `navigation` entries to be dispatched.
po.observe({type: 'navigation', buffered: true});

Ein weiterer Messwert, der für Entwickler von Service Workern von Bedeutung sein kann, ist die Startzeit des Service Workers für Navigationsanfragen. Das ist die Zeit, die der Browser benötigt, um den Service Worker-Thread zu starten, bevor er Fetch-Ereignisse abfangen kann.

Die Service Worker-Startzeit für eine bestimmte Navigationsanfrage kann aus der Differenz zwischen entry.responseStart und entry.workerStart ermittelt werden.

// Create the performance observer.
const po = new PerformanceObserver((list) => {
  for (const entry of list.getEntries()) {
    console.log('Service Worker startup time:',
        entry.responseStart - entry.workerStart);
  }
});

// Start listening for `navigation` entries to be dispatched.
po.observe({type: 'navigation', buffered: true});

Server Timing API

Unterstützte Browser

  • Chrome: 65
  • Edge: 79.
  • Firefox: 61.
  • Safari: 16.4.

Quelle

Mit der Server Timing API können Sie anfragespezifische Zeitdaten über Antwortheader von Ihrem Server an den Browser weitergeben. Sie können beispielsweise angeben, wie lange es gedauert hat, Daten für eine bestimmte Anfrage in einer Datenbank abzurufen. Das kann beim Beheben von Leistungsproblemen hilfreich sein, die durch eine langsame Serverleistung verursacht werden.

Für Entwickler, die Analysetools von Drittanbietern verwenden, ist die Server Timing API die einzige Möglichkeit, Serverleistungsdaten mit anderen Geschäftsmesswerten in Beziehung zu setzen, die mit diesen Analysetools erfasst werden.

Sie können den Antwortheader Server-Timing verwenden, um Server-Timing-Daten in Ihren Antworten anzugeben. Hier ein Beispiel:

HTTP/1.1 200 OK

Server-Timing: miss, db;dur=53, app;dur=47.2

Anschließend können Sie diese Daten auf Ihren Seiten sowohl in resource- als auch in navigation-Einträgen aus den Resource Timing API und Navigation Timing API lesen.

// Create the performance observer.
const po = new PerformanceObserver((list) => {
  for (const entry of list.getEntries()) {
    // Logs all server timing data for this response
    console.log('Server Timing', entry.serverTiming);
  }
});

// Start listening for `navigation` entries to be dispatched.
po.observe({type: 'navigation', buffered: true});