API für Frames für lange Animationen

Die Long Animation Frames API (LoAF, ausgesprochen Lo-Af) ist ein Update der Long Tasks API, mit dem sich langsame Updates der Benutzeroberfläche besser nachvollziehen lassen. Das kann hilfreich sein, um langsame Animationsframes zu identifizieren, die sich wahrscheinlich auf den Core Web Vital-Messwert Interaction to Next Paint (INP) auswirken, mit dem die Reaktionsfähigkeit gemessen wird, oder um andere Ruckler in der Benutzeroberfläche zu identifizieren, die sich auf die Glaubwürdigkeit auswirken.

Status der API

Browser Support

  • Chrome: 123.
  • Edge: 123.
  • Firefox: not supported.
  • Safari: not supported.

Source

Nach einem Ursprungstest von Chrome 116 bis Chrome 122 wurde die LoAF API ab Chrome 123 eingeführt.

Hintergrund: Long Tasks API

Browser Support

  • Chrome: 58.
  • Edge: 79.
  • Firefox: not supported.
  • Safari: not supported.

Source

Die Long Animation Frames API ist eine Alternative zur Long Tasks API, die seit Chrome 58 in Chrome verfügbar ist. Wie der Name schon sagt, können Sie mit der Long Task API lange Aufgaben überwachen, also Aufgaben, die den Haupt-Thread mindestens 50 Millisekunden lang belegen. Lange Aufgaben können über die Benutzeroberfläche PerformanceLongTaskTiming mit einem PeformanceObserver überwacht werden:

const observer = new PerformanceObserver((list) => {
  console.log(list.getEntries());
});

observer.observe({ type: 'longtask', buffered: true });

Lange Aufgaben führen wahrscheinlich zu Problemen mit der Reaktionsfähigkeit. Wenn ein Nutzer versucht, mit einer Seite zu interagieren, z. B. auf eine Schaltfläche zu klicken oder ein Menü zu öffnen, der Hauptthread aber bereits mit einer langen Aufgabe beschäftigt ist, wird die Interaktion des Nutzers verzögert, bis diese Aufgabe abgeschlossen ist.

Um die Reaktionsfähigkeit zu verbessern, wird oft empfohlen, lange Aufgaben aufzuteilen. Wenn jede lange Aufgabe stattdessen in eine Reihe von mehreren kleineren Aufgaben unterteilt wird, können zwischendurch wichtigere Aufgaben ausgeführt werden, um erhebliche Verzögerungen bei der Reaktion auf Interaktionen zu vermeiden.

Wenn Sie also die Reaktionsfähigkeit verbessern möchten, führen Sie zuerst eine Leistungsanalyse durch und sehen Sie sich lange Aufgaben an. Dazu können Sie ein labbasiertes Analysetool wie Lighthouse verwenden, das die Prüfung Lange Aufgaben im Hauptthread vermeiden enthält, oder sich lange Aufgaben in den Chrome DevTools ansehen.

Labortests sind häufig kein guter Ausgangspunkt, um Probleme mit der Reaktionsfähigkeit zu identifizieren, da diese Tools möglicherweise keine Interaktionen enthalten. Falls doch, handelt es sich um eine kleine Auswahl wahrscheinlicher Interaktionen. Im Idealfall sollten Sie die Ursachen für langsame Interaktionen vor Ort messen.

Nachteile der Long Tasks API

Das Messen langer Aufgaben im Feld mit einem Leistungs-Observer ist nur bedingt sinnvoll. In Wirklichkeit gibt es nicht viele Informationen darüber, außer dass eine lange Aufgabe stattgefunden hat und wie lange sie gedauert hat.

Mithilfe von Real User Monitoring (RUM)-Tools werden häufig Trends bei der Anzahl oder Dauer langer Aufgaben ermittelt oder die Seiten identifiziert, auf denen sie auftreten. Ohne die zugrunde liegenden Details zur Ursache der langen Aufgabe ist dies jedoch nur von begrenztem Nutzen. Die Long Tasks API hat nur ein einfaches Attributionsmodell, das bestenfalls nur den Container angibt, in dem die lange Aufgabe ausgeführt wurde (das Dokument der obersten Ebene oder eine <iframe>), aber nicht das Script oder die Funktion, die sie aufgerufen hat. Das zeigt ein typischer Eintrag:

{
  "name": "unknown",
  "entryType": "longtask",
  "startTime": 31.799999997019768,
  "duration": 136,
  "attribution": [
    {
      "name": "unknown",
      "entryType": "taskattribution",
      "startTime": 0,
      "duration": 0,
      "containerType": "window",
      "containerSrc": "",
      "containerId": "",
      "containerName": ""
    }
  ]
}

Die Long Tasks API ist ebenfalls unvollständig, da einige wichtige Aufgaben möglicherweise ausgeschlossen werden. Einige Aktualisierungen, z. B. das Rendering, erfolgen in separaten Aufgaben, die idealerweise zusammen mit der vorherigen Ausführung, die diese Aktualisierung verursacht hat, enthalten sein sollten, um die „Gesamtarbeit“ für diese Interaktion genau zu messen. Weitere Informationen zu den Einschränkungen von Aufgaben finden Sie in der Erläuterung im Abschnitt „Wo lange Aufgaben versagen“.

Das letzte Problem ist, dass bei der Messung langer Aufgaben nur einzelne Aufgaben erfasst werden, die länger als 50 Millisekunden dauern. Ein Animationsframe kann aus mehreren Aufgaben bestehen, die kürzer als 50 Millisekunden sind, aber gemeinsam das Rendern des Browsers blockieren.

Die Long Animation Frames API

Browser Support

  • Chrome: 123.
  • Edge: 123.
  • Firefox: not supported.
  • Safari: not supported.

Source

Die Long Animation Frames API (LoAF) ist eine neue API, mit der einige der Mängel der Long Tasks API behoben werden sollen. So erhalten Entwickler umsetzbarere Informationen, um Probleme mit der Reaktionsfähigkeit zu beheben und die INP zu verbessern. Außerdem erhalten sie Informationen zu Problemen mit der Laufruhe.

Eine gute Reaktionsfähigkeit bedeutet, dass eine Seite schnell auf Interaktionen reagiert. Dazu müssen alle vom Nutzer benötigten Aktualisierungen zeitnah angezeigt werden und es darf keine Blockierungen geben. Für INP wird eine Antwortzeit von maximal 200 Millisekunden empfohlen. Bei anderen Aktualisierungen (z. B. Animationen) sind aber selbst 200 Millisekunden möglicherweise zu lang.

Die Long Animation Frames API ist eine alternative Methode zur Messung von blockierenden Arbeiten. Anstatt die einzelnen Aufgaben zu messen, werden mit der Long Animation Frames API – wie der Name schon sagt – lange Animationsframes gemessen. Ein langer Animationsframe liegt vor, wenn eine Renderingaktualisierung länger als 50 Millisekunden verzögert ist (derselbe Grenzwert wie für die Long Tasks API).

Lange Animationsframes werden vom Beginn der Aufgaben gemessen, für die ein Rendern erforderlich ist. Wenn für die erste Aufgabe in einem potenziellen langen Animationsframe kein Rendern erforderlich ist, wird der lange Animationsframe nach Abschluss der Aufgabe ohne Rendering beendet und ein neuer potenzieller langer Animationsframe wird mit der nächsten Aufgabe gestartet. Solche nicht gerenderten langen Animationsframes werden in der Long Animation Frames API weiterhin berücksichtigt, wenn sie länger als 50 Millisekunden sind (mit einer renderStart-Zeit von 0), um die Messung potenziell blockierender Arbeit zu ermöglichen.

Lange Animationsframes können ähnlich wie lange Aufgaben mit einem PerformanceObserver beobachtet werden, wobei stattdessen der Typ long-animation-frame betrachtet wird:

const observer = new PerformanceObserver((list) => {
  console.log(list.getEntries());
});

observer.observe({ type: 'long-animation-frame', buffered: true });

Vorherige Frames mit langen Animationen können auch über die Leistungszeitachse abgefragt werden:

const loafs = performance.getEntriesByType('long-animation-frame');

Es gibt jedoch eine maxBufferSize für Leistungseinträge, nach der neuere Einträge gelöscht werden. Daher wird der PerformanceObserver-Ansatz empfohlen. Die Puffergröße für long-animation-frame ist auf 200 festgelegt, genau wie für long-tasks.

Vorteile der Betrachtung von Frames anstelle von Aufgaben

Der Hauptvorteil dieser Betrachtungsweise aus der Perspektive des Frames statt der Aufgaben besteht darin, dass eine lange Animation aus beliebig vielen Aufgaben bestehen kann, die zusammen zu einem langen Animationsframe führen. Dies behebt den letzten oben genannten Punkt, bei dem die Summe vieler kleinerer, renderblockierender Aufgaben vor einem Animationsframe möglicherweise nicht von der Long Tasks API erfasst wird.

Ein weiterer Vorteil dieser alternativen Ansicht für lange Aufgaben ist die Möglichkeit, Zeitaufschlüsselungen für den gesamten Frame bereitzustellen. Anstatt nur startTime und duration wie bei der Long Tasks API enthält LoAF eine viel detailliertere Aufschlüsselung der verschiedenen Teile der Framedauer.

Zeitstempel und Dauer von Frames

  • startTime: die Startzeit des langen Animationsframes relativ zur Navigationsstartzeit.
  • duration: die Dauer des langen Animationsframes (ohne Präsentationszeit).
  • renderStart: die Startzeit des Rendering-Zyklus, einschließlich requestAnimationFrame-Callbacks, Stil- und Layoutberechnung, Resize-Observer- und Intersection-Observer-Callbacks.
  • styleAndLayoutStart: Beginn des Zeitraums, der für Stil- und Layoutberechnungen aufgewendet wurde.
  • firstUIEventTimestamp: Die Zeit des ersten UI-Ereignisses (Maus/Tastatur usw.), das während dieses Frames verarbeitet wird. Hinweis: Das firstUIEventTimestamp kann sich in einem vorherigen Frame befinden, wenn zwischen dem Ereignis und der Verarbeitung eine Verzögerung aufgetreten ist.
  • blockingDuration: die Gesamtdauer in Millisekunden, für die der Animationsframe die Verarbeitung von Eingaben oder anderen Aufgaben mit hoher Priorität blockiert.

Eine Erklärung zu blockingDuration

Ein langer Animationsframe kann aus mehreren Aufgaben bestehen. blockingDuration ist die Summe der Aufgabendauern, die länger als 50 Millisekunden sind (einschließlich der endgültigen Renderdauer innerhalb der längsten Aufgabe).

Wenn ein langer Animationsframe beispielsweise aus zwei Aufgaben mit 55 Millisekunden und 65 Millisekunden besteht, gefolgt von einem Rendern von 20 Millisekunden, beträgt die duration etwa 140 Millisekunden mit einer blockingDuration von (55 − 50) + (65 + 20 − 50) = 40 Millisekunden. Während dieses 140 Millisekunden langen Animationsframes wurde der Frame 40 Millisekunden lang als blockiert für die Verarbeitung von Eingaben betrachtet.

Ob duration oder blockingDuration betrachtet werden soll

Bei einem 60-Hertz-Display versucht ein Browser, mindestens alle 16,66 Millisekunden einen Frame zu planen (um flüssige Aktualisierungen zu ermöglichen) oder nach einer Aufgabe mit hoher Priorität wie der Eingabeverarbeitung (um responsive Aktualisierungen zu ermöglichen). Wenn es jedoch keine Eingaben und keine anderen Aufgaben mit hoher Priorität gibt, aber eine Warteschlange mit anderen Aufgaben vorhanden ist, fährt der Browser den aktuellen Frame in der Regel weit über 16,66 Millisekunden fort, unabhängig davon, wie gut die Aufgaben darin aufgeteilt sind. Das heißt, der Browser versucht immer, Eingaben zu priorisieren, kann aber auch eine Aufgabenliste anstelle von Rendering-Aktualisierungen bearbeiten. Das liegt daran, dass das Rendering ein kostspieliger Prozess ist. Die Verarbeitung einer kombinierten Rendering-Aufgabe für mehrere Aufgaben führt daher in der Regel zu einer insgesamt geringeren Arbeitsbelastung.

Daher sollten lange Animationsframes mit einem niedrigen oder nullwertigen blockingDuration immer noch reaktionsschnell auf Eingaben reagieren. Die Reduzierung oder Beseitigung von blockingDuration durch Aufteilen langer Aufgaben ist daher entscheidend, um die Reaktionsfähigkeit gemäß INP zu verbessern.

Viele lange Animationsframes, unabhängig von blockingDuration, deuten jedoch auf verzögerte UI-Aktualisierungen hin, die sich dennoch auf die Laufruhe auswirken und zu einer verzögerten Benutzeroberfläche beim Scrollen oder bei Animationen führen können, auch wenn dies für die Reaktionsfähigkeit, gemessen anhand des INP, weniger problematisch ist. Um Probleme in diesem Bereich zu verstehen, sehen Sie sich die duration an. Diese lassen sich jedoch schwieriger optimieren, da Sie das Problem nicht durch Aufteilen der Arbeit lösen können, sondern die Arbeit reduzieren müssen.

Frame-Timings

Mit den zuvor erwähnten Zeitstempeln kann der lange Animationsframe in Zeiträume unterteilt werden:

Timing Berechnung
Beginn startTime
Ende startTime + duration
Arbeitsdauer renderStart ? renderStart - startTime : duration
Renderingdauer renderStart ? (startTime + duration) - renderStart: 0
Rendern: Dauer vor dem Layout styleAndLayoutStart ? styleAndLayoutStart - renderStart : 0
Rendern: Dauer von Stil und Layout styleAndLayoutStart ? (startTime + duration) - styleAndLayoutStart : 0

Bessere Scriptzuordnung

Der long-animation-frame-Eintragstyp enthält bessere Attributionsdaten für jedes Script, das zu einem langen Animationsframe beigetragen hat (für Scripts, die länger als 5 Millisekunden sind).

Ähnlich wie bei der Long Tasks API werden diese Daten in einer Reihe von Attributionseinträgen bereitgestellt, die jeweils folgende Details enthalten:

  • Sowohl name als auch EntryType geben script zurück.
  • Eine aussagekräftige invoker, die angibt, wie das Script aufgerufen wurde (z. B. 'IMG#id.onload', 'Window.requestAnimationFrame' oder 'Response.json.then').
  • Die invokerType des Script-Einstiegspunkts:
    • user-callback: Ein bekannter Callback, der über eine Webplattform-API registriert wurde (z. B. setTimeout, requestAnimationFrame).
    • event-listener: Ein Listener für ein Plattformereignis (z. B. click, load, keyup).
    • resolve-promise: Handler eines Plattformversprechens (z. B. fetch(). Hinweis: Bei Promises werden alle Handler derselben Promises als ein „Script“ zusammengeführt..
    • reject-promise: Wie bei resolve-promise, aber für die Ablehnung.
    • classic-script: Scriptauswertung (z. B. <script> oder import())
    • module-script: Entspricht classic-script, aber für Modulscripts.
  • Separate Zeitdaten für dieses Script:
    • startTime: Zeitpunkt, zu dem die Eingabefunktion aufgerufen wurde.
    • duration: Die Dauer zwischen startTime und dem Abschluss der Verarbeitung der nachfolgenden Microtask-Warteschlange.
    • executionStart: Die Zeit nach der Kompilierung.
    • forcedStyleAndLayoutDuration: Die Gesamtzeit, die für die Verarbeitung des erzwungenen Layouts und Stils in dieser Funktion aufgewendet wird (siehe Überlastung).
    • pauseDuration: Gesamtzeit, die für die Pausierung synchroner Vorgänge (Benachrichtigung, synchrone XHR) aufgewendet wurde.
  • Details zur Scriptquelle:
    • sourceURL: Der Name der Scriptressource, sofern verfügbar (leer, wenn nicht gefunden).
    • sourceFunctionName: Der Name der Scriptfunktion, sofern verfügbar (leer, wenn nicht gefunden).
    • sourceCharPosition: Die Position des Schriftzeichens, sofern verfügbar (oder -1, wenn es nicht gefunden wurde).
  • windowAttribution: Der Container (das Dokument auf oberster Ebene oder ein <iframe>), in dem der lange Animationsframe aufgetreten ist.
  • window: Eine Referenz auf das Fenster mit demselben Ursprung.

Sofern vorhanden, können Entwickler anhand der Quelleinträge genau nachvollziehen, wie jedes Script im langen Animationsframe aufgerufen wurde, bis hin zur Zeichenposition im aufrufenden Script. Hier sehen Sie den genauen Speicherort in einer JavaScript-Ressource, der zu dem langen Animationsframe geführt hat.

Beispiel für einen long-animation-frame-Leistungseintrag

Hier ein vollständiges Beispiel für einen long-animation-frame-Leistungseintrag mit einem einzelnen Script:

{
  "blockingDuration": 0,
  "duration": 60,
  "entryType": "long-animation-frame",
  "firstUIEventTimestamp": 11801.099999999627,
  "name": "long-animation-frame",
  "renderStart": 11858.800000000745,
  "scripts": [
    {
      "duration": 45,
      "entryType": "script",
      "executionStart": 11803.199999999255,
      "forcedStyleAndLayoutDuration": 0,
      "invoker": "DOMWindow.onclick",
      "invokerType": "event-listener",
      "name": "script",
      "pauseDuration": 0,
      "sourceURL": "https://web.dev/js/index-ffde4443.js",
      "sourceFunctionName": "myClickHandler",
      "sourceCharPosition": 17796,
      "startTime": 11803.199999999255,
      "window": [Window object],
      "windowAttribution": "self"
    }
  ],
  "startTime": 11802.400000000373,
  "styleAndLayoutStart": 11858.800000000745
}

Wie Sie sehen, erhalten Sie so eine nie dagewesene Menge an Daten, mit denen Sie die Ursache für verzögerte Rendering-Aktualisierungen ermitteln können.

Long Animation Frames API im Feld verwenden

Tools wie die Chrome-Entwicklertools und Lighthouse sind zwar nützlich, um Probleme zu erkennen und zu reproduzieren, aber sie sind Lab-Tools, die wichtige Aspekte der Nutzererfahrung übersehen können, die nur Felddaten liefern können.

Die Long Animation Frames API wurde entwickelt, um wichtige Kontextdaten für Nutzerinteraktionen zu erfassen, die mit der Long Tasks API nicht möglich sind. So können Sie Probleme mit Interaktivität erkennen und reproduzieren, die Sie sonst möglicherweise nicht entdeckt hätten.

Unterstützung der API für die Funktion zur Erkennung langer Frames bei Animationen

Mit dem folgenden Code kannst du testen, ob die API unterstützt wird:

if (PerformanceObserver.supportedEntryTypes.includes('long-animation-frame')) {
  // Monitor LoAFs
}

Der offensichtlichste Anwendungsfall für die Long Animation Frames API besteht darin, Probleme mit der Interaktion bis zur nächsten Darstellung (Interaction to Next Paint, INP) zu diagnostizieren und zu beheben. Dies war einer der Hauptgründe, warum das Chrome-Team diese API entwickelt hat. Bei einer guten INP werden alle Interaktionen innerhalb von 200 Millisekunden nach der Interaktion bis zur Darstellung des Frames verarbeitet. Da die Long Animation Frames API alle Frames misst, die mindestens 50 Millisekunden dauern, sollten die meisten problematischen INPs LoAF-Daten enthalten, damit Sie diese Interaktionen diagnostizieren können.

Das „INP LoAF“ ist das LoAF, das die INP-Interaktion enthält, wie im folgenden Diagramm dargestellt:

Beispiele für lange Animationsframes auf einer Seite, wobei der INP-LoAF hervorgehoben ist.
Eine Seite kann viele LoAFs haben, von denen eine mit der INP-Interaktion zusammenhängt.

In einigen Fällen kann ein INP-Ereignis zwei LoAFs umfassen. Das ist in der Regel der Fall, wenn die Interaktion nach dem Start des Rendering-Teils des vorherigen Frames erfolgt. Der Ereignis-Handler wird dann im nächsten Frame verarbeitet:

Beispiele für lange Animationsframes auf einer Seite, wobei der INP-LoAF hervorgehoben ist.
Eine Seite kann viele LoAFs haben, von denen eine mit der INP-Interaktion zusammenhängt.

In seltenen Fällen kann es sogar mehr als zwei LoAFs umfassen.

Wenn Sie die Daten der LoAFs erfassen, die mit der INP-Interaktion verknüpft sind, erhalten Sie viel mehr Informationen zur INP-Interaktion, die Ihnen bei der Diagnose helfen. Das ist besonders hilfreich, um die Eingabeverzögerung zu verstehen, da Sie sehen können, welche anderen Scripts in diesem Frame ausgeführt wurden.

Es kann auch hilfreich sein, die Ursache für eine unerklärte Verarbeitungsdauer und Darstellungsverzögerung zu ermitteln, wenn Ihre Ereignis-Handler die dafür angezeigten Werte nicht reproduzieren. Möglicherweise werden für Ihre Nutzer andere Scripts ausgeführt, die nicht in Ihren eigenen Tests enthalten sind.

Es gibt keine direkte API, um einen INP-Eintrag mit den zugehörigen LoAF-Einträgen zu verknüpfen. Es ist jedoch möglich, dies im Code zu tun, indem die Start- und Endzeiten der einzelnen Einträge verglichen werden (siehe WhyNp). Die web-vitals-Bibliothek enthält alle sich überschneidenden LoAFs in der longAnimationFramesEntries-Property der INP-Attributionsoberfläche ab Version 4.

Nachdem Sie den oder die LoAF-Einträge verknüpft haben, können Sie Informationen mit INP-Attribution einfügen. Das scripts-Objekt enthält einige der wertvollsten Informationen, da es Aufschluss darüber geben kann, was sonst noch in diesen Frames ausgeführt wurde. Wenn Sie diese Daten an Ihren Analysedienst senden, können Sie besser nachvollziehen, warum Interaktionen langsam waren.

Wenn Sie LoAFs für die INP-Interaktion melden, können Sie die dringendsten Interaktivitätsprobleme auf Ihrer Seite ermitteln. Jeder Nutzer kann unterschiedlich mit Ihrer Seite interagieren. Bei einer ausreichenden Menge an INP-Attributionsdaten sind in den INP-Attributionsdaten eine Reihe potenzieller Probleme enthalten. So können Sie Scripts nach Volumen sortieren, um zu sehen, welche Scripts mit einer langsamen INP korrelieren.

Längere Animationsdaten an einen Analyseendpunkt zurückgeben

Ein Nachteil der ausschließlichen Betrachtung der INP-LoAFs besteht darin, dass Sie möglicherweise andere potenzielle Verbesserungsmöglichkeiten übersehen, die zu zukünftigen INP-Problemen führen können. Das kann dazu führen, dass Sie das Gefühl haben, im Kreis zu laufen, wenn Sie ein INP-Problem beheben und eine große Verbesserung erwarten, nur um festzustellen, dass die nächste langsamste Interaktion nur geringfügig besser ist, sodass sich Ihr INP nicht wesentlich verbessert.

Anstatt sich nur auf den INP-LoAF zu konzentrieren, sollten Sie alle LoAFs über die gesamte Lebensdauer der Seite hinweg berücksichtigen:

Eine Seite mit vielen LoAFs, von denen einige während Interaktionen auftreten, auch wenn es sich nicht um die INP-Interaktion handelt.
Wenn Sie sich alle LoAFs ansehen, können Sie zukünftige INP-Probleme leichter erkennen.

Jeder LoAF-Eintrag enthält jedoch erhebliche Daten. Daher sollten Sie Ihre Analyse wahrscheinlich auf einige LoAFs beschränken. Da die Einträge für lange Animationsframes außerdem recht groß sein können, sollten Entwickler entscheiden, welche Daten aus dem Eintrag an Analytics gesendet werden sollen. Dazu gehören beispielsweise die Zusammenfassungszeiten des Eintrags und möglicherweise die Scriptnamen oder andere Mindestmengen an anderen Kontextdaten, die als erforderlich erachtet werden.

Hier einige Vorschläge, wie Sie die Menge der Daten für lange Animationsframes reduzieren können:

Welches dieser Muster am besten für Sie geeignet ist, hängt davon ab, wie weit Sie mit der Optimierung fortgeschritten sind und wie häufig lange Animationsframes vorkommen. Bei einer Website, die noch nie für die Responsivität optimiert wurde, kann es viele LoAFs geben. Sie können sich auf LoAFs mit Interaktionen beschränken, einen hohen Grenzwert festlegen oder sich nur die schlechtesten ansehen.

Wenn Sie die häufigsten Probleme mit der Reaktionsfähigkeit behoben haben, können Sie die Funktion erweitern, indem Sie sie nicht nur auf Interaktionen oder lange Blockierungsdauern beschränken oder die Grenzwerte senken.

Lange Animationsframes mit Interaktionen beobachten

Wenn Sie nicht nur Informationen zum INP-Long-Animation-Frame erhalten möchten, können Sie sich alle LoAFs mit Interaktionen (die am Vorhandensein eines firstUIEventTimestamp-Werts erkannt werden können) mit einem hohen blockingDuration ansehen.

Dies kann auch eine einfachere Methode sein, INP-LoAFs zu überwachen, anstatt zu versuchen, die beiden zu korrelieren, was komplexer sein kann. In den meisten Fällen ist dies die LoAF für die Interaktion mit dem Anzeigeninventar für einen bestimmten Besuch. In seltenen Fällen, in denen dies nicht der Fall ist, werden trotzdem lange Interaktionen angezeigt, die behoben werden müssen, da sie für andere Nutzer die Interaktion mit dem Anzeigeninventar sein könnten.

Der folgende Code protokolliert alle LoAF-Einträge mit einer blockingDuration von mehr als 100 Millisekunden, bei denen während des Frames eine Interaktion stattgefunden hat. Die 100 wird hier ausgewählt, da sie unter dem INP-Grenzwert von 200 Millisekunden liegt. Je nach Bedarf können Sie einen höheren oder niedrigeren Wert auswählen.

const REPORTING_THRESHOLD_MS = 100;

const observer = new PerformanceObserver(list => {
  for (const entry of list.getEntries()) {
    if (entry.blockingDuration > REPORTING_THRESHOLD_MS &&
      entry.firstUIEventTimestamp > 0
    ) {
      // Example here logs to console, but could also report back to analytics
      console.log(entry);
    }
  }
});
observer.observe({ type: 'long-animation-frame', buffered: true });

Lange Animationsframes mit hoher Blockierungsdauer beobachten

Anstatt sich alle langen Frames mit Interaktionen anzusehen, sollten Sie sich alle langen Frames mit langen Blockierungsdauern ansehen. Dies weist auf potenzielle INP-Probleme hin, wenn ein Nutzer während dieser langen Animationsframes interagiert.

Der folgende Code protokolliert alle LoAF-Einträge mit einer Blockierungsdauer von mehr als 100 Millisekunden, bei denen während des Frames eine Interaktion stattgefunden hat. Die Zahl 100 wird hier ausgewählt, da sie unter dem INP-Grenzwert von 200 Millisekunden liegt, um potenzielle Problemframes zu identifizieren und gleichzeitig die Anzahl der gemeldeten langen Animationsframes auf ein Minimum zu beschränken. Je nach Bedarf können Sie einen höheren oder niedrigeren Wert auswählen.

const REPORTING_THRESHOLD_MS = 100;

const observer = new PerformanceObserver(list => {
  for (const entry of list.getEntries()) {
    if (entry.blockingDuration > REPORTING_THRESHOLD_MS) {
      // Example here logs to console, but could also report back to analytics
      console.log(entry);
    }
  }
});
observer.observe({ type: 'long-animation-frame', buffered: true });

Lange Animationsframes bei wichtigen UI-Änderungen beobachten, um die Laufruhe zu verbessern

Wie bereits erwähnt, kann die Prüfung langer Frames mit hoher Blockierungsdauer dazu beitragen, die Eingabereaktion zu verbessern. Für eine flüssige Animation sollten Sie sich jedoch alle langen Animationsframes mit einer langen duration ansehen.

Da dies zu sehr ungenauen Ergebnissen führen kann, sollten Sie die Messungen auf wichtige Punkte mit einem Muster wie diesem beschränken:

const REPORTING_THRESHOLD_MS = 100;

const observer = new PerformanceObserver(list => {
  if (measureImportantUIupdate) {
    for (const entry of list.getEntries()) {
      if (entry.duration > REPORTING_THRESHOLD_MS) {
        // Example here logs to console, but could also report back to analytics
        console.log(entry);
      }
    }
  }
});
observer.observe({ type: 'long-animation-frame', buffered: true });

async function doUIUpdatesWithMeasurements() {
  measureImportantUIupdate = true;
  await doUIUpdates();
  measureImportantUIupdate = false;
}

Die schlechtesten Long-Animation-Frames beobachten

Anstatt einen Grenzwert festzulegen, können Websites Daten für den längsten Animationsframe (oder Frames) erfassen, um das Volumen der Daten zu reduzieren, die per Beacon gesendet werden müssen. Unabhängig davon, wie viele lange Animationsframes auf einer Seite verwendet werden, werden nur Daten für die fünf, zehn oder wie viele langen Animationsframes auch immer absolut notwendig sind, zurückgesendet.

MAX_LOAFS_TO_CONSIDER = 10;
let longestBlockingLoAFs = [];

const observer = new PerformanceObserver(list => {
  longestBlockingLoAFs = longestBlockingLoAFs.concat(list.getEntries()).sort(
    (a, b) => b.blockingDuration - a.blockingDuration
  ).slice(0, MAX_LOAFS_TO_CONSIDER);
});
observer.observe({ type: 'long-animation-frame', buffered: true });

Diese Strategien können auch kombiniert werden. Sehen Sie sich nur die zehn schlechtesten LoAFs mit Interaktionen an, die länger als 100 Millisekunden dauern.

Senden Sie das Beacon zum richtigen Zeitpunkt (idealerweise beim visibilitychange-Ereignis) zurück an Analytics. Für lokale Tests können Sie console.table regelmäßig verwenden:

console.table(longestBlockingLoAFs);

Gängige Muster in langen Animationsframes identifizieren

Eine alternative Strategie wäre, sich gängige Scripts anzusehen, die am häufigsten in langen Animationsframe-Einträgen vorkommen. Daten können auf Script- und Zeichenpositionsebene gemeldet werden, um Wiederholungstäter zu identifizieren.

Das funktioniert möglicherweise besonders gut für anpassbare Plattformen, auf denen Themen oder Plug-ins, die Leistungsprobleme verursachen, auf einer Reihe von Websites identifiziert werden können.

Die Ausführungszeit gängiger Scripts oder Drittanbieter-Quellen in langen Animationsframes kann summiert und zurückgemeldet werden, um häufige Ursachen für lange Animationsframes auf einer Website oder einer Gruppe von Websites zu identifizieren. So können Sie sich beispielsweise URLs ansehen:

const observer = new PerformanceObserver(list => {
  const allScripts = list.getEntries().flatMap(entry => entry.scripts);
  const scriptSource = [...new Set(allScripts.map(script => script.sourceURL))];
  const scriptsBySource= scriptSource.map(sourceURL => ([sourceURL,
      allScripts.filter(script => script.sourceURL === sourceURL)
  ]));
  const processedScripts = scriptsBySource.map(([sourceURL, scripts]) => ({
    sourceURL,
    count: scripts.length,
    totalDuration: scripts.reduce((subtotal, script) => subtotal + script.duration, 0)
  }));
  processedScripts.sort((a, b) => b.totalDuration - a.totalDuration);
  // Example here logs to console, but could also report back to analytics
  console.table(processedScripts);
});

observer.observe({type: 'long-animation-frame', buffered: true});

Ein Beispiel für diese Ausgabe:

(index) sourceURL count totalDuration
0 'https://example.consent.com/consent.js' 1 840
1 'https://example.com/js/analytics.js' 7 628
2 'https://example.chatapp.com/web-chat.js' 1 5

Long Animation Frames API in Tools verwenden

Die API bietet auch zusätzliche Entwicklertools für die lokale Fehlerbehebung. Mit einigen Tools wie Lighthouse und Chrome DevTools konnten bereits viele dieser Daten mithilfe von Details auf niedriger Ebene erfasst werden. Mit dieser API auf höherer Ebene könnten jedoch auch andere Tools auf diese Daten zugreifen.

Daten zu Long Animation Frames in den Entwicklertools aufrufen

Mit der performance.measure() API können Sie lange Animationsframes in den DevTools anzeigen lassen. Diese werden dann in Leistungsaufzeichnungen im Zeitverlauf für Nutzer angezeigt, um zu zeigen, wo Sie sich auf Leistungsverbesserungen konzentrieren sollten. Mit der DevTools Extensibility API können sie sogar in einem eigenen Track angezeigt werden:

const observer = new PerformanceObserver((list) => {
  for (const entry of list.getEntries()) {
    performance.measure('LoAF', {
      start: entry.startTime,
      end: entry.startTime + entry.duration,
      detail: {
        devtools: {
          dataType: "track-entry",
          track: "Long animation frames",
          trackGroup: "Performance Timeline",
          color: "tertiary-dark",
          tooltipText: 'LoAF'
        }
      }
    });
  }
});

observer.observe({ type: 'long-animation-frame', buffered: true });
DevTools-Leistungspanel-Trace mit einem benutzerdefinierten Track, der Daten zu langen Animationsframes enthält, die mit dem Haupt-Flaschendiagramm verglichen werden können.
Lange Animationsframe-Daten in den DevTools anzeigen

Langfristig werden lange Animationsframes wahrscheinlich in die DevTools selbst eingebunden. Mit dem vorherigen Code-Snippet können sie aber in der Zwischenzeit dort angezeigt werden.

Der erste Eintrag in der vorherigen Abbildung zeigt auch, wo der Browser mehrere Aufgaben im selben langen Animationsframe verarbeitet hat, anstatt zwischen ihnen zu rendern. Wie bereits erwähnt, kann dies passieren, wenn es keine Eingabeaufgaben mit hoher Priorität gibt, aber eine Warteschlange mit Aufgaben vorhanden ist. Für die erste lange Aufgabe müssen einige Rendering-Aktualisierungen ausgeführt werden, da andernfalls der aktuelle lange Animationsframe danach zurückgesetzt und mit der nächsten Aufgabe ein neuer gestartet würde. Anstatt dieses Rendering sofort auszuführen, hat der Browser jedoch eine Reihe zusätzlicher Aufgaben verarbeitet und erst dann die lange Rendering-Aufgabe ausgeführt und den langen Animationsframe beendet. Das zeigt, wie nützlich es ist, sich in den DevTools lange Animationsframes anzusehen, anstatt nur lange Aufgaben, um verzögerte Renderings zu identifizieren.

Daten zu langen Animationsframes in anderen Entwicklertools verwenden

Die Web Vitals-Erweiterung hat den Wert in den Protokollierungs-Debugging-Informationen der Zusammenfassung angezeigt, um Leistungsprobleme zu diagnostizieren.

Außerdem werden jetzt für jeden INP-Callback und jede Interaktion Daten zu langen Animationsframes angezeigt:

Console-Logging der Web Vitals-Erweiterung
In der Konsole der Web Vitals-Erweiterung werden LoAF-Daten protokolliert.

Daten zu langen Animationsframes in automatisierten Testtools verwenden

Ähnlich können automatisierte Testtools in CI/CD-Pipelines Details zu potenziellen Leistungsproblemen aufzeigen, indem lange Animationsframes während der Ausführung verschiedener Test-Suites gemessen werden.

FAQ

Zu den häufig gestellten Fragen zu dieser API gehören:

Warum nicht einfach die Long Tasks API erweitern oder iterieren?

Dies ist eine alternative Möglichkeit, eine ähnliche, aber letztendlich andere Messung potenzieller Probleme mit der Reaktionsfähigkeit zu erfassen. Es ist wichtig, dass Websites, die auf die bestehende Long Tasks API angewiesen sind, weiterhin funktionieren, um Unterbrechungen bestehender Anwendungsfälle zu vermeiden.

Die Long Tasks API kann zwar von einigen der Funktionen von LoAF profitieren (z. B. einem besseren Attributionsmodell), wir sind jedoch der Meinung, dass der Fokus auf Frames statt auf Aufgaben viele Vorteile bietet, die diese API grundlegend von der bestehenden Long Tasks API unterscheiden.

Why do I not have script entries?

Dies kann darauf hindeuten, dass der lange Animationsframe nicht auf JavaScript, sondern auf eine große Menge an Rendering zurückzuführen ist.

Das kann auch passieren, wenn der lange Animationsframe auf JavaScript zurückzuführen ist, die Script-Attribution aber aus verschiedenen Datenschutzgründen, wie bereits erwähnt, nicht angegeben werden kann (vor allem, wenn das JavaScript nicht der Seite gehört).

Warum habe ich Scripteinträge, aber keine oder nur wenige Quelleninformationen?

Das kann verschiedene Gründe haben, z. B. dass keine gute Quelle angegeben wurde.

Bei no-cors cross-origin-Scripts werden die Skriptinformationen auf die sourceURL (ohne Weiterleitungen) beschränkt. Für die sourceFunctionName wird ein leerer String und für die sourceCharPosition ein -1 verwendet. Dieses Problem lässt sich beheben, indem du diese Scripts mit CORS abgreifst. Dazu musst du dem <script>-Aufruf crossOrigin = "anonymous" hinzufügen.

Beispiel für das Standard-Google Tag Manager-Script, das der Seite hinzugefügt werden soll:

<!-- Google Tag Manager -->
<script>(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
})(window,document,'script','dataLayer','GTM-XXXXXXX');</script>
<!-- End Google Tag Manager -->

Kann durch Hinzufügen von j.crossOrigin = "anonymous" erweitert werden, um vollständige Attributionsdetails für GTM bereitzustellen.

Wird die Long Tasks API dadurch ersetzt?

Wir sind der Meinung, dass die Long Animation Frames API eine bessere, umfassendere API für die Messung langer Aufgaben ist. Derzeit ist jedoch nicht geplant, die Long Tasks API einzustellen.

Feedback erwünscht

Feedback kann in der GitHub-Problemliste gepostet werden. Fehler in der Chrome-Implementierung der API können im Chrome-Issue-Tracker gemeldet werden.

Fazit

Die Long Animation Frames API ist eine spannende neue API mit vielen potenziellen Vorteilen gegenüber der vorherigen Long Tasks API.

Es erweist sich als wichtiges Tool zur Behebung von Problemen mit der Reaktionsfähigkeit, gemessen am INP. INP ist ein Messwert, der sich nur schwer optimieren lässt. Mit dieser API möchte das Chrome-Team Entwicklern die Identifizierung und Behebung von Problemen erleichtern.

Der Umfang der Long Animation Frames API geht jedoch über INP hinaus und kann helfen, andere Ursachen für langsame Aktualisierungen zu identifizieren, die sich auf die allgemeine Nutzerfreundlichkeit einer Website auswirken können.

Danksagungen

Miniaturansicht von Henry Be auf Unsplash.