Benutzerdefinierte Messwerte

nutzerbezogene Messwerte, die sich auf jeder Website universell messen lassen, sind äußerst nützlich. Mit diesen Messwerten können Sie:

  • Verstehen, wie echte Nutzende das Web als Ganzes erleben.
  • Vergleichen Sie Ihre Website mit der eines Mitbewerbers.
  • Verfolgen Sie nützliche und umsetzbare Daten in Ihren Analysetools, 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 messen, um die Nutzererfahrung auf einer bestimmten Website umfassend erfassen zu können.

Mit benutzerdefinierten Messwerten können Sie die Nutzererfahrung auf Ihrer Website ermitteln, die möglicherweise nur für sie gilt, z. B.:

  • Wie lange es dauert, bis eine Single-Page-Anwendung (SPA) von einer Seite übergeht in einen anderen.
  • Wie lange es dauert, bis auf einer Seite Daten angezeigt werden, die aus einer Datenbank für angemeldete Nutzer abgerufen wurden.
  • Gibt an, wie lange es dauert, bis eine SSR-App (Server-Side-Rendering) hydratisiert.
  • Die Cache-Trefferquote für Ressourcen, die von wiederkehrenden Besuchern geladen wurden.
  • Die Ereignislatenz von Klick- oder Tastaturereignissen in einem Spiel.

APIs zum Messen benutzerdefinierter Messwerte

Bisher hatten Webentwickler nicht viele Low-Level-APIs zur Leistungsmessung und mussten daher auf Hacks zurückgreifen, um die Leistung einer Website zu messen.

Sie können beispielsweise bestimmen, ob der Hauptthread aufgrund von JavaScript-Aufgaben mit langer Ausführungszeit blockiert ist, indem Sie eine requestAnimationFrame-Schleife ausführen und das Delta zwischen den einzelnen Frames berechnen. Wenn das Delta deutlich länger ist als die Framerate der Anzeige, 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. Daher sollten Sie für alle benutzerdefinierten Messwerte, die Sie auf Ihrer Website messen, nach Möglichkeit eine der folgenden APIs verwenden.

Performance Observer API

Unterstützte Browser

  • 52
  • 79
  • 57
  • 11

Quelle

Mit der Performance Observer API werden Daten aus allen anderen auf dieser Seite beschriebenen Leistungs-APIs erfasst und angezeigt. Es ist entscheidend, sie zu verstehen, um aussagekräftige Daten zu erhalten.

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

Übergeben Sie zum Erstellen von PerformanceObserver einen Callback, der immer dann ausgeführt wird, wenn neue Leistungseinträge gesendet werden. Dann teilen Sie dem Beobachter mithilfe der Methode observe() mit, welche Arten von Einträgen überwacht werden 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 sind alle Eintragstypen aufgeführt, die zur Beobachtung verfügbar sind. In neueren Browsern können Sie jedoch prüfen, welche Eintragstypen über die statische Eigenschaft PerformanceObserver.supportedEntryTypes verfügbar sind.

Einträge beobachten, die bereits stattgefunden haben

Standardmäßig können PerformanceObserver-Objekte nur Einträge beobachten, während sie auftreten. 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 ältere Einträge abrufen möchten (nachdem sie erfasst wurden), setzen Sie das Flag buffered auf true, wenn Sie observe() aufrufen. Der Browser schließt beim ersten Aufruf des PerformanceObserver-Callbacks Verlaufsdaten aus seinem Zwischenspeicher für Leistungseinträge ein, bis hin zur maximalen Zwischenspeichergröße für diesen Typ.

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

Zu vermeidende Legacy-Performance-APIs

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

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 verfügbar gemacht, sondern nur über PerformanceObserver.

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

Nutzertiming-API

Unterstützte Browser

  • 28
  • 12
  • 38
  • 11

Quelle

Die User Timing API ist ein Measurement API für zeitbasierte Messwerte. Damit können Sie beliebig Punkte in 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 liegt jedoch darin, dass sie sich gut in Leistungstools einbinden lässt. In den Chrome-Entwicklertools werden beispielsweise Messungen des Nutzertimings im Bereich „Leistung“ visualisiert. Viele Analyseanbieter erfassen außerdem automatisch alle von Ihnen vorgenommenen Messungen und senden die Daten zur Dauer an ihr Analyse-Backend.

Wenn Sie Nutzertiming-Messungen in Berichte aufnehmen möchten, können Sie PerformanceObserver verwenden und sich registrieren, um Einträge vom Typ measure 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 `measure` entries to be dispatched.
po.observe({type: 'measure', buffered: true});

Long Tasks API

Unterstützte Browser

  • 58
  • 79
  • x
  • x

Quelle

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

Jedes Mal, wenn Sie teuren Code ausführen oder große Skripts laden und ausführen müssen, ist es hilfreich zu verfolgen, ob dieser Code den Hauptthread blockiert. Tatsächlich basieren viele übergeordnete Messwerte auf der Long Tasks API selbst, z. B. Time to Interactive (TTI) und Total Blocking Time (TBT)).

Um zu ermitteln, wann lange Aufgaben stattfinden, können Sie PerformanceObserver verwenden und sich registrieren, um Einträge vom Typ longtask 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 `longtask` entries to be dispatched.
po.observe({type: 'longtask', buffered: true});

Long Animation Frames API

Unterstützte Browser

  • 123
  • 123
  • x
  • x

Quelle

Die Long Animation Frames API ist eine neue Iteration der Long Tasks API, die anstelle von langen Aufgaben lange Frames mit mehr als 50 Millisekunden berücksichtigt. 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

  • 77
  • 79
  • x
  • x

Quelle

Der Messwert Largest Contentful Paint (LCP) gibt Aufschluss darüber, wann das größte Bild oder der größte Textblock auf den Bildschirm gezeichnet 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 tatsächlich auf der Element Timing API und fügt automatisch das größte inhaltsorientierte Element hinzu. Sie können aber auch Berichte zu anderen Elementen erstellen, indem Sie ihnen explizit das Attribut elementtiming hinzufügen und einen PerformanceObserver registrieren, um den Eintragstyp element 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

  • 76
  • 79
  • 89
  • x

Quelle

Mit dem Messwert Interaction to Next Paint (INP) wird die Reaktionsfähigkeit einer Seite insgesamt bewertet. Dabei werden alle Interaktionen per Klick, Tippen und über die Tastatur während der gesamten Lebensdauer einer Seite erfasst. Der INP einer Seite ist meist die Interaktion, die am längsten gedauert hat, von dem Zeitpunkt, an dem der Nutzer die Interaktion initiiert hat, bis zu dem Zeitpunkt, an dem der Browser den nächsten Frame rendert, der das visuelle Ergebnis der Nutzereingabe anzeigt.

Der INP-Messwert wird durch die Event Timing API ermöglicht. Diese API stellt eine Reihe von Zeitstempeln bereit, die während des Ereignislebenszyklus auftreten, darunter:

  • startTime: die Uhrzeit, zu der der Browser das Ereignis empfängt
  • processingStart: die Zeit, zu der der Browser mit der Verarbeitung von Event-Handlern für das Ereignis beginnen kann.
  • processingEnd: Zeitpunkt, zu dem der Browser die Ausführung des gesamten synchronen Codes beendet, der von Event-Handlern für dieses Ereignis initiiert wurde.
  • duration: die Zeit (aus Sicherheitsgründen auf 8 Millisekunden gerundet) zwischen dem Empfang des Ereignisses im Browser, bis der nächste Frame dargestellt werden kann, nachdem der gesamte synchrone Code ausgeführt wurde, der von den Event-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

  • 29
  • 12
  • 35
  • 11

Quelle

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

  • initiatorType: Wie die Ressource abgerufen wurde, beispielsweise über ein <script>- oder <link>-Tag oder einen fetch()-Aufruf.
  • nextHopProtocol: Das Protokoll, mit dem die Ressource abgerufen wird, z. B. h2 oder quic.
  • encodedBodySize/decodedBodySize]: die Größe der Ressource in ihrer codierten bzw. decodierten Form.
  • transferSize: die Größe der Ressource, die tatsächlich über das Netzwerk übertragen wurde. Wenn die Ressourcen vom Cache bedient werden, kann dieser Wert viel kleiner als der encodedBodySize sein. In einigen Fällen kann er null sein (wenn keine erneute Cache-Validierung erforderlich ist).

Sie können das Attribut transferSize von Ressourcenzeiteinträgen verwenden, um einen Messwert für die Cache-Trefferquote oder die Gesamtgröße der im Cache gespeicherten Ressourcen zu messen. Dies kann hilfreich sein, um zu verstehen, wie sich Ihre Caching-Strategie für Ressourcen auf die Leistung wiederkehrender Besucher auswirkt.

Im folgenden Beispiel werden alle von der Seite angeforderten Ressourcen protokolliert und es wird angegeben, ob jede Ressource vom Cache erfüllt wurde.

// 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

  • 57
  • 12
  • 58
  • 15

Quelle

Die Navigation Timing API ähnelt der Resource Timing API, meldet jedoch nur Navigationsanfragen. Der Eintragstyp navigation ähnelt dem Eintragstyp resource, enthält jedoch 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 verfolgen, um die Serverantwortzeit (Time to First Byte (TTFB)) zu verstehen, ist mit der Navigation Timing API verfügbar, insbesondere mit dem Zeitstempel responseStart 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, den Entwickler, die Service Worker verwenden, interessieren könnte, ist die Service Worker-Startzeit für Navigationsanfragen. Dies ist die Zeit, die der Browser benötigt, um den Service Worker-Thread zu starten, bevor er Abrufereignisse 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

  • 65
  • 79
  • 61
  • 16.4

Quelle

Mit der Server Timing API kannst du anfragespezifische Zeitdaten von deinem Server über Antwortheader an den Browser weitergeben. Sie können beispielsweise angeben, wie lange es gedauert hat, Daten in einer Datenbank für eine bestimmte Anfrage zu suchen. Dies kann beim Beheben von Leistungsproblemen hilfreich sein, die durch langsame Serveraktivitäten verursacht werden.

Für Entwickler, die Analyseanbieter von Drittanbietern nutzen, ist die Server Timing API die einzige Möglichkeit, Daten zur Serverleistung mit anderen Geschäftsmesswerten zu korrelieren, die von diesen Analysetools möglicherweise gemessen werden.

Wenn Sie in Ihren Antworten Serverzeitdaten angeben möchten, können Sie den Antwortheader Server-Timing verwenden. 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 der Resource Timing API und der 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});