Nutzertiming-API

Web-App verstehen

Alex Danilo

Hochleistungs-Webanwendungen sind entscheidend für eine gute User Experience. Webanwendungen werden immer komplexer. Um ein überzeugendes Erlebnis zu schaffen, ist es daher entscheidend, die Auswirkungen auf die Leistung zu verstehen. In den letzten Jahren sind verschiedene APIs im Browser aufgetaucht, um die Leistung des Netzwerks, Ladezeiten usw. zu analysieren. Diese liefern jedoch nicht unbedingt detaillierte Details und ausreichend Flexibilität, um herauszufinden, was Ihre Anwendung verlangsamt. Geben Sie die User Timing API ein. Diese bietet einen Mechanismus, mit dem Sie Ihre Webanwendung steuern können, um zu ermitteln, wo sie ihre Zeit verbringt. In diesem Artikel stellen wir die API sowie Beispiele für ihre Verwendung vor.

Was sich nicht messen lässt, kann nicht optimiert werden

Der erste Schritt zur Beschleunigung einer langsamen Webanwendung besteht darin, herauszufinden, wo Zeit aufgewendet wird. Die Messung der zeitlichen Auswirkungen von einzelnen Bereichen des JavaScript-Codes ist die ideale Methode, um Hotspots zu erkennen. Dies ist der erste Schritt zur Verbesserung der Leistung. Glücklicherweise bietet die User Timing API die Möglichkeit, API-Aufrufe an verschiedenen Stellen in Ihrem JavaScript einzufügen und dann detaillierte Zeitdaten für die Optimierung zu extrahieren.

Zeit für hohe Auflösung und now()

Ein wesentlicher Bestandteil der genauen Zeitmessung ist die Genauigkeit. Früher basierte das Timing auf Messungen im Millisekundenbereich, was in Ordnung ist, aber um eine Website ohne Verzögerungen mit 60 fps zu erstellen, bedeutete das, dass jeder Frame in 16 ms gezeichnet werden muss. Wenn nur eine Genauigkeit im Millisekundenbereich vorhanden ist, fehlt die Präzision, die für eine gute Analyse erforderlich ist. Geben Sie High Resolution Time ein. Dies ist ein neuer Zeittyp, der in modernen Browsern integriert ist. Die Zeit in hoher Auflösung liefert uns Gleitkomma-Zeitstempel, die bis zu Mikrosekunden genau sind – tausendmal besser als zuvor.

Um die aktuelle Uhrzeit in Ihrer Webanwendung zu erhalten, rufen Sie die Methode now() auf. Diese bildet eine Erweiterung der Leistung-Schnittstelle. Der folgende Code zeigt, wie das funktioniert:

var myTime = window.performance.now();

Es gibt eine weitere Schnittstelle namens PerformanceTiming, die verschiedene Zeitpunkte für das Laden Ihrer Webanwendung bereitstellt. Die Methode now() gibt die Zeit zurück, die seit dem Auftreten des navigationStart-Zeitraums in PerformanceTiming verstrichen ist.

Typ „DOMHighResTimeStamp“

Bei der Zeitmessung von Webanwendungen haben Sie in der Vergangenheit beispielsweise Date.now() verwendet, das einen DOMTimeStamp zurückgibt. DOMTimeStamp gibt eine Ganzzahl in Millisekunden als Wert zurück. Um die höhere Genauigkeit für die Zeit in hoher Auflösung zu ermöglichen, wurde ein neuer Typ namens DOMHighResTimeStamp eingeführt. Dieser Typ ist ein Gleitkommawert, der auch die Zeit in Millisekunden zurückgibt. Da es sich um einen Gleitkommawert handelt, kann der Wert Bruchteile von Millisekunden darstellen und so eine Genauigkeit von einer Tausendstel Millisekunde ergeben.

Die Benutzeroberfläche für das Nutzertiming

Da wir nun Zeitstempel mit hoher Auflösung haben, rufen wir jetzt über die Schnittstelle Nutzertiming die Zeitinformationen ab.

Die User Timing-Oberfläche bietet Funktionen, mit denen wir Methoden an verschiedenen Stellen in unserer Anwendung aufrufen können. Sie können eine Breadcrumb-Spur nach Hänsel- und Gretel-Stil bereitstellen, damit wir nachverfolgen können, wo die Zeit verbracht wurde.

mark() verwenden

Die mark()-Methode ist das wichtigste Tool in unserem Zeitanalyse-Toolkit. mark() speichert einen Zeitstempel für uns. Das Tolle an mark() ist, dass wir dem Zeitstempel einen Namen geben können und die API den Namen und den Zeitstempel als eine Einheit speichert.

Wenn Sie mark() an verschiedenen Stellen in Ihrer Anwendung aufrufen, können Sie herausfinden, wie lange es gedauert hat, bis Sie diese Markierung in Ihrer Webanwendung erreicht haben.

Die Spezifikation enthält eine Reihe vorgeschlagener Namen für Marken, die interessant sein können und ziemlich selbsterklärend sind, z. B. mark_fully_loaded,mark_fully_visible, mark_above_the_fold usw.

Mithilfe des folgenden Codes können wir beispielsweise eine Markierung für den vollständigen Ladevorgang der Anwendung setzen:

window.performance.mark('mark_fully_loaded');

Durch das Setzen benannter Markierungen in unserer Webanwendung können wir eine ganze Reihe von Zeitangaben sammeln und diese in unserer Freizeit analysieren, um herauszufinden, was die Anwendung wann tut.

Messungen mit measure() berechnen

Wenn Sie eine Reihe von Zeitmarkierungen festgelegt haben, möchten Sie sicherlich die zwischen ihnen verstrichene Zeit ermitteln. Dazu verwenden Sie die Methode measure().

Die Methode measure() berechnet die verstrichene Zeit zwischen Markierungen und kann auch die Zeit zwischen Ihrer Markierung und einem der bekannten Ereignisnamen in der PerformanceTiming-Schnittstelle messen.

Sie können beispielsweise die Zeit vom Fertigstellen des DOM bis zum vollständigen Laden des Anwendungsstatus mithilfe von Code wie dem folgenden ermitteln:

window.performance.measure('measure_load_from_dom', 'domComplete', 'mark_fully_loaded');

Wenn Sie measure() aufrufen, wird das Ergebnis unabhängig von den festgelegten Markierungen gespeichert, sodass Sie sie später abrufen können. Indem Sie die Zeiten für die Ausführung Ihrer Anwendung speichern, bleibt die Anwendung reaktionsschnell und Sie können alle Daten ausgeben, nachdem die Anwendung einige Arbeit beendet hat, damit sie später analysiert werden können.

Markierungen mit clearMarks() werden verworfen

Manchmal ist es hilfreich, wenn Sie eine Reihe von Markierungen entfernen. Sie können beispielsweise Batchausführungen in Ihrer Webanwendung ausführen und daher bei jeder Ausführung von vorn beginnen.

Rufen Sie einfach clearMarks() an, um Markierungen wieder zu entfernen.

Der Beispielcode unten würde also all Ihre vorhandenen Markierungen weglöschen, sodass Sie bei Bedarf erneut eine zeitliche Ausführung einrichten können.

window.performance.clearMarks();

Natürlich gibt es einige Szenarien, in denen du möglicherweise nicht alle Punkte löschen möchtest. Wenn Sie also bestimmte Markierungen entfernen möchten, können Sie einfach den Namen der Markierung übergeben, die Sie entfernen möchten. Hier sehen Sie ein Beispiel für den folgenden Code:

window.peformance.clearMarks('mark_fully_loaded');

entfernt die Markierung, die wir im ersten Beispiel festgelegt haben, und lässt alle anderen Markierungen unverändert.

Falls Sie Ihre Messungen aufheben möchten, gibt es dafür eine entsprechende Methode namens clearMeasures(). Sie funktioniert genau wie bei clearMarks(), nur dass sie an deinen Messungen arbeitet. Hier ein Beispiel mit dem Code:

window.performance.clearMeasures('measure_load_from_dom');

wird die Messung aus dem obigen measure()-Beispiel entfernt. Wenn Sie alle Kennzahlen entfernen möchten, funktioniert dies wie bei clearMarks(), nur dass Sie clearMeasures() ohne Argumente aufrufen.

Zeitangaben abrufen

Es ist alles in Ordnung und gut, Markierungen zu setzen und Intervalle zu messen, aber irgendwann sollten Sie Daten zu diesem Zeitpunkt erhalten, um eine Analyse durchzuführen. Das ist auch ganz einfach. Sie brauchen nur die PerformanceTimeline-Oberfläche zu verwenden.

Mit der Methode getEntriesByType() können wir beispielsweise alle Zeiten für die Markierung abrufen oder die Zeitüberschreitung für alle unsere Messungen wird als Liste erfasst, damit wir sie iterieren und die Daten verarbeiten können. Das Tolle ist, dass die Liste in chronologischer Reihenfolge zurückgegeben wird, sodass Sie die Markierungen in der Reihenfolge sehen können, in der sie in Ihrer Webanwendung getroffen wurden.

Der Code unten:

var items = window.performance.getEntriesByType('mark');

gibt eine Liste aller Markierungen zurück, die in unserer Webanwendung gefunden wurden, und der Code:

var items = window.performance.getEntriesByType('measure');

uns eine Liste aller durchgeführten Messungen zurück.

Du kannst auch eine Liste der Einträge abrufen, indem du den spezifischen Namen verwendest, den du ihnen gegeben hast. Zum Beispiel der Code:

var items = window.performance.getEntriesByName('mark_fully_loaded');

uns eine Liste mit einem Element zurück, das den Zeitstempel „mark_vollständig_Lade“ in der Eigenschaft startTime enthält.

Zeit für eine XHR-Anfrage (Beispiel)

Nachdem wir nun ein gutes Bild von der User Timing API haben, können wir damit analysieren, wie lange all unsere XMLHttpRequests in unserer Webanwendung dauern.

Zuerst ändern wir alle unsere send()-Anfragen, um einen Funktionsaufruf auszugeben, der die Markierungen einrichtet. Gleichzeitig ändern wir unsere Erfolgs-Callbacks mit einem Funktionsaufruf, der eine weitere Markierung setzt und dann ein Maß dafür generiert, wie lange die Anfrage gedauert hat.

Normalerweise würde unser XMLHttpRequest also in etwa so aussehen:

var myReq = new XMLHttpRequest();
myReq.open('GET', url, true);
myReq.onload = function(e) {
  do_something(e.responseText);
}
myReq.send();

In unserem Beispiel fügen wir einen globalen Zähler hinzu, um die Anzahl der Anfragen zu verfolgen und ihn auch zum Speichern einer Messung für jede gesendete Anfrage zu verwenden. Der Code hierfür sieht wie folgt aus:

var reqCnt = 0;

var myReq = new XMLHttpRequest();
myReq.open('GET', url, true);
myReq.onload = function(e) {
  window.performance.mark('mark_end_xhr');
  reqCnt++;
  window.performance.measure('measure_xhr_' + reqCnt, 'mark_start_xhr', 'mark_end_xhr');
  do_something(e.responseText);
}
window.performance.mark('mark_start_xhr');
myReq.send();

Der obige Code generiert eine Messung mit einem eindeutigen Namenswert für jede von uns gesendete XMLHttpRequest. Wir gehen davon aus, dass die Anfragen nacheinander ausgeführt werden – der Code für parallele Anfragen müsste etwas komplexer sein, um Anfragen zu verarbeiten, die nicht in der richtigen Reihenfolge zurückgegeben werden. Wir belassen dies als Übung für den Leser.

Sobald die Webanwendung eine Reihe von Anfragen ausgeführt hat, können Sie alle mit dem folgenden Code an die Konsole übertragen:

var items = window.performance.getEntriesByType('measure');
for (var i = 0; i < items.length; ++i) {
  var req = items[i];
  console.log('XHR ' + req.name + ' took ' + req.duration + 'ms');
}

Fazit

Die User Timing API bietet Ihnen zahlreiche großartige Tools, die Sie auf jeden Aspekt Ihrer Webanwendung anwenden können. Sie können die Hotspots in Ihrer Anwendung leicht eingrenzen, indem Sie API-Aufrufe in Ihrer Webanwendung verteilen und die generierten Zeitdaten nachverarbeiten, um ein klares Bild davon zu erhalten, wo Zeit aufgewendet wird. Aber was ist, wenn Ihr Browser diese API nicht unterstützt? Kein Problem, hier findest du einen guten Polyfill, der die API gut emuliert und sich auch mit webpagetest.org gut fühlt. Sind Sie neugierig geworden? Probieren Sie die User Timing API jetzt in Ihren Anwendungen aus. Sie werden herausfinden, wie Sie diese beschleunigen können, und Ihre Nutzer werden es Ihnen danken, dass Sie ihre Anwendungen so viel besser gemacht haben.