Benutzerdefinierte Messwerte

Mit universellen, nutzerorientierten Messwerten, die Sie auf jeder Website messen können, können Sie die Erfahrungen Ihrer Nutzer im Web nachvollziehen und Ihre Website mit der Ihrer Mitbewerber vergleichen. In vielen Fällen müssen Sie jedoch mehr als nur die universellen Messwerte erfassen, um das gesamte Erlebnis für Ihre spezifische Website zu erfassen.

Mit benutzerdefinierten Messwerten lassen sich Aspekte der Nutzererfahrung messen, die möglicherweise nur für Ihre Website gelten, z. B.:

  • Legt fest, wie lange es dauert, bis eine Single-Page-Anwendung (SPA) von einer „Seite“ zu einer anderen wechselt.
  • Legt fest, wie lange es dauert, bis auf einer Seite die aus einer Datenbank abgerufenen Daten für angemeldete Nutzer angezeigt werden.
  • Die Zeitdauer, bis eine serverseitig gerenderte (SSR) App hydriert wird.
  • 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

Webentwickler hatten bisher nicht viele Low-Level-APIs zum Messen der Leistung und mussten deshalb auf Hacks zurückgreifen, um die Leistung einer Website zu messen. Sie können beispielsweise feststellen, ob der Hauptthread durch lange laufende JavaScript-Aufgaben blockiert wird. Dazu führen Sie eine requestAnimationFrame-Schleife aus und berechnen das Delta zwischen den einzelnen Frames. Wenn das Delta deutlich länger als die Framerate der Anzeige ist, können Sie dies als lange Aufgabe melden.

Solche Hacks können jedoch die Leistung deiner Website beeinträchtigen, beispielsweise dadurch, dass der Akku des Geräts entladen wird. Wenn Ihre Methoden zur Leistungsmessung selbst Leistungsprobleme verursachen, sind die Daten, die Sie daraus erhalten, nicht korrekt. Daher empfehlen wir, zum Erstellen benutzerdefinierter Messwerte eine der folgenden APIs zu verwenden.

Performance Observer API

Unterstützte Browser

  • 52
  • 79
  • 57
  • 11

Quelle

Die Performance Observer API ist der Mechanismus, mit dem Daten aus allen anderen auf dieser Seite beschriebenen Performance APIs erhoben und angezeigt werden. Diese zu verstehen, ist entscheidend, um gute Daten zu erhalten.

Mit PerformanceObserver können Sie leistungsbezogene Ereignisse passiv abonnieren. So können API-Callbacks während inaktiven Zeiträumen ausgelöst werden, was die Seitenleistung in der Regel nicht beeinträchtigt.

Übergeben Sie beim Erstellen eines PerformanceObserver einen Callback, der immer dann ausgeführt wird, wenn neue Leistungseinträge gesendet werden. Verwenden Sie dann die Methode observe(), um dem Beobachter mitzuteilen, welche Arten von Einträgen überwacht werden sollen:

// Catch errors that some browsers throw when using the new `type` option.
// https://bugs.webkit.org/show_bug.cgi?id=209216
try {
  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'});
} catch (e) {
  // Do nothing if the browser doesn't support this API.
}

In den folgenden Abschnitten werden alle Eintragstypen aufgeführt, die Sie beobachten können. In neueren Browsern können Sie auch mit dem statischen Attribut PerformanceObserver.supportedEntryTypes prüfen, welche Eintragstypen verfügbar sind.

Einträge ansehen, die bereits vorhanden sind

Standardmäßig können PerformanceObserver-Objekte Einträge nur beim Auftreten beobachten. Dies kann zu Problemen führen, wenn Sie Ihren Code für Leistungsanalysen per Lazy Loading laden möchten, damit er keine Ressourcen mit höherer Priorität blockiert.

Wenn Sie Verlaufsdaten abrufen möchten, rufen Sie observe auf und setzen Sie das Flag buffered auf true. Der Browser schließt dann Verlaufseinträge aus seinem Puffer für Leistungseinträge ein, wenn der PerformanceObserver-Callback zum ersten Mal aufgerufen wird.

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

Zu vermeidende Legacy Performance APIs

Vor der Performance Observer API konnten Entwickler mit den folgenden, für das Objekt performance definierten Methoden auf Leistungseinträge zugreifen. Wir raten von ihrer Verwendung ab, da sie dir nicht erlauben, auf neue Einträge zu warten.

Darüber hinaus werden viele neue APIs (z. B. Long Tasks) nicht vom Objekt performance, sondern nur von PerformanceObserver verfügbar gemacht. Daher empfiehlt es sich, diese Methoden in Ihrem Code zu vermeiden und in Zukunft PerformanceObserver zu verwenden, es sei denn, Sie benötigen ausdrücklich Internet Explorer-Kompatibilität.

User Timing API

Die User Timing API ist eine universelle API zur Messung zeitbasierter Messwerte. Sie können damit beliebige Zeitpunkte markieren und später die Dauer zwischen diesen Markierungen 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');

Obwohl APIs wie Date.now() oder performance.now() ähnliche Funktionen bieten, ist die User Timing API besser in Leistungstools eingebunden. In den Chrome-Entwicklertools werden beispielsweise Messungen des Nutzertimings im Bereich „Leistung“ visualisiert. Viele Analyseanbieter erfassen automatisch alle Messungen, die Sie vornehmen, und senden die Dauerdaten an ihr Analyse-Backend.

Wenn Sie Berichte zum Nutzertiming erstellen möchten, registrieren Sie einen PerformanceObserver, um Einträge vom Typ measure zu beobachten:

// Catch errors some browsers throw when using the new `type` option.
// https://bugs.webkit.org/show_bug.cgi?id=209216
try {
  // 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.
  po.observe({type: 'measure', buffered: true});
} catch (e) {
  // Do nothing if the browser doesn't support this API.
}

Long Tasks API

Unterstützte Browser

  • 58
  • 79
  • x
  • x

Quelle

Mit der Long Tasks API lässt sich feststellen, wann der Hauptthread des Browsers lange genug blockiert ist, um sich auf die Framerate oder die Eingabelatenz zu auswirken. Die API meldet alle Aufgaben, die länger als 50 Millisekunden (ms) 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 prüfen, ob dieser Code den Hauptthread blockiert. Tatsächlich basieren viele übergeordnete Messwerte wie Time to Interactive (TTI) und Total Blocking Time (TBT) auf der Long Tasks API selbst.

Wenn Sie feststellen möchten, wann lange Aufgaben stattfinden, registrieren Sie einen PerformanceObserver, um Einträge vom Typ longtask zu beobachten:

// Catch errors since some browsers throw when using the new `type` option.
// https://bugs.webkit.org/show_bug.cgi?id=209216
try {
  // 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});
} catch (e) {
  // Do nothing if the browser doesn't support this API.
}

Element Timing API

Unterstützte Browser

  • 77
  • 79
  • x
  • x

Quelle

Der Messwert Largest Contentful Paint (LCP) ist hilfreich, wenn Sie wissen möchten, wann das größte Bild oder der größte Textblock Ihrer Seite auf dem Bildschirm angezeigt wird. In einigen Fällen möchten Sie aber auch die Renderingzeit eines anderen Elements messen.

Verwenden Sie für diese Fälle die Element Timing API. Die LCP API baut auf der Element Timing API auf und fügt automatische Berichte des größten inhaltsreichen Elements hinzu. Sie können aber auch Berichte zu anderen Elementen erstellen, indem Sie ihnen explizit das Attribut elementtiming hinzufügen und einen PerformanceObserver registrieren, der den Eintragstyp element beobachtet.

<img elementtiming="hero-image" />
<p elementtiming="important-paragraph">This is text I care about.</p>
...
<script>
// Catch errors since some browsers throw when using the new `type` option.
// https://bugs.webkit.org/show_bug.cgi?id=209216
try {
  // Create the performance observer.
  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});
} catch (e) {
  // Do nothing if the browser doesn't support this API.
}
</script>

Event Timing API

Der Messwert First Input Delay (FID) misst die Zeit von der ersten Interaktion eines Nutzers mit einer Seite bis zu dem Zeitpunkt, zu dem der Browser als Reaktion auf diese Interaktion Event-Handler verarbeiten kann. In manchen Fällen kann es jedoch auch nützlich sein, die Ereignisverarbeitungszeit selbst zu messen.

Dies ist mit der Event Timing API möglich, die nicht nur die FID erfasst, sondern auch eine Reihe von Zeitstempeln im Ereignislebenszyklus bereitstellt, darunter:

  • startTime: die Zeit, zu der das Ereignis im Browser empfangen wird.
  • processingStart: Die Zeit, zu der der Browser mit der Verarbeitung von Event-Handlern für das Ereignis beginnen kann.
  • processingEnd: Die Zeit, zu der der Browser den gesamten von Event-Handlern initiierten synchronen Code für dieses Ereignis ausgeführt hat.
  • duration: Die Zeit (aus Sicherheitsgründen auf 8 ms gerundet) zwischen dem Zeitpunkt, an dem das Ereignis im Browser eingeht, bis er den nächsten Frame darstellen kann, nachdem der gesamte von den Event-Handlern initiierte synchrone Code ausgeführt wurde.

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

// Catch errors some browsers throw when using the new `type` option.
// https://bugs.webkit.org/show_bug.cgi?id=209216
try {
  const po = new PerformanceObserver((entryList) => {
    const firstInput = entryList.getEntries()[0];

    // Measure First Input Delay (FID).
    const firstInputDelay = firstInput.processingStart - firstInput.startTime;

    // Measure the time it takes to run all event handlers
    // Doesn't include work scheduled asynchronously using methods like
    // `requestAnimationFrame()` or `setTimeout()`.
    const firstInputProcessingTime = firstInput.processingEnd - firstInput.processingStart;

    // Measure the entire duration of the event, from when input is received by
    // the browser until the next frame can be painted after processing all
    // event handlers.
    // Doesn't include work scheduled asynchronously using
    // `requestAnimationFrame()` or `setTimeout()`.
    // For security reasons, this value is rounded to the nearest 8 ms.
    const firstInputDuration = firstInput.duration;

    // Log these values to the console.
    console.log({
      firstInputDelay,
      firstInputProcessingTime,
      firstInputDuration,
    });
  });

  po.observe({type: 'first-input', buffered: true});
} catch (error) {
  // Do nothing if the browser doesn't support this API.
}

Resource Timing API

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 darin enthaltenen Informationen nicht nur auf Zeitdaten beschränkt (auch wenn es viele davon gibt). Weitere Daten, auf die Sie zugreifen können, sind:

  • initiatorType: Gibt an, wie die Ressource abgerufen wurde, z. B. aus einem <script>- oder <link>-Tag oder von fetch().
  • nextHopProtocol: das zum Abrufen der Ressource verwendete Protokoll, z. B. h2 oder quic.
  • encodedBodySize und 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 Ressourcen mit dem Cache gefüllt werden, kann dieser Wert viel kleiner als encodedBodySize sein. In einigen Fällen kann er auch null sein, wenn keine erneute Cache-Validierung erforderlich ist.

Mit dem Attribut transferSize von Einträgen für das Ressourcentiming können Sie einen Messwert für die Cache-Trefferrate oder einen Messwert für die Gesamtgröße der im Cache gespeicherten Ressource messen. So können Sie nachvollziehen, wie sich Ihre Strategie für das Ressourcen-Caching auf die Leistung für wiederkehrende Besucher auswirkt.

Im folgenden Beispiel werden alle von der Seite angeforderten Ressourcen protokolliert. Dabei wird angegeben, ob jede Ressource über den Cache erfüllt wurde oder nicht:

// Catch errors some browsers throw when using the new `type` option.
// https://bugs.webkit.org/show_bug.cgi?id=209216
try {
  // Create the performance observer.
  const po = new PerformanceObserver((list) => {
    for (const entry of list.getEntries()) {
      // If transferSize is 0, the resource was fulfilled via the cache.
      console.log(entry.name, entry.transferSize === 0);
    }
  });
  // Start listening for `resource` entries to be dispatched.
  po.observe({type: 'resource', buffered: true});
} catch (e) {
  // Do nothing if the browser doesn't support this API.
}

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 auch dem Eintragstyp resource, enthält jedoch einige zusätzliche Informationen, die nur für Navigationsanfragen relevant sind, z. B. wenn die Ereignisse DOMContentLoaded und load ausgelöst werden.

Ein Messwert, den viele Entwickler verfolgen, um die Serverantwortzeit zu verstehen, Time to First Byte (TTFB), ist über den Zeitstempel responseStart in der Navigation Timing API verfügbar.

// Catch errors since  browsers throw when using the new `type` option.
// https://bugs.webkit.org/show_bug.cgi?id=209216
try {
  // 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});
} catch (e) {
  // Do nothing if the browser doesn't support this API.
}

Ein weiterer Messwertentwickler, der Service Worker verwenden kann, ist die Startzeit von Service Workern 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 angegebene Navigationsanfrage kann so aus dem Delta zwischen entry.responseStart und entry.workerStart ermittelt werden:

// Catch errors some browsers throw when using the new `type` option.
// https://bugs.webkit.org/show_bug.cgi?id=209216
try {
  // 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});
} catch (e) {
  // Do nothing if the browser doesn't support this API.
}

Server Timing API

Mit der Server Timing API können Sie anfragespezifische Zeitdaten mithilfe von Antwortheadern von Ihrem Server an den Browser übergeben. Sie können beispielsweise angeben, wie lange die Suche nach Daten in einer Datenbank für eine bestimmte Anfrage gedauert hat. Dies kann beim Beheben von Leistungsproblemen hilfreich sein, die durch eine langsame Servergeschwindigkeit verursacht werden.

Für Entwickler, die externe Analyseanbieter verwenden, ist die Server Timing API die einzige Möglichkeit, Serverleistungsdaten mit anderen Geschäftsmesswerten in Beziehung zu setzen, die diese Analysetools messen.

Wenn du Servertiming-Daten in deinen Antworten angeben möchtest, verwende den Antwortheader Server-Timing. Beispiel:

HTTP/1.1 200 OK

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

Anschließend können Sie auf Ihren Seiten diese Daten zu resource- und navigation-Einträgen der Resource Timing API und der Navigation Timing API lesen.

// Catch errors some browsers throw when using the new `type` option.
// https://bugs.webkit.org/show_bug.cgi?id=209216
try {
  // 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});
} catch (e) {
  // Do nothing if the browser doesn't support this API.
}