Erfahren Sie, wie Sie langsame Interaktionen in den Felddaten Ihrer Website finden, um Möglichkeiten zur Verbesserung der Interaktion mit Next Paint zu finden.
Felddaten geben Aufschluss darüber, wie echte Nutzer Ihre Website erleben. So lassen sich Probleme aufdecken, die sich nur mit Lab-Daten nicht finden lassen. Bei Interaction to Next Paint (INP) sind Felddaten unerlässlich, um langsame Interaktionen zu identifizieren und wichtige Hinweise zur Behebung zu erhalten.
In diesem Leitfaden erfahren Sie, wie Sie mithilfe von Felddaten aus dem Bericht zur Nutzererfahrung in Chrome (Chrome User Experience, CrUX) schnell die INP Ihrer Website bewerten und feststellen können, ob es Probleme mit der INP gibt. Anschließend erfahren Sie, wie Sie die Attributionsversion der JavaScript-Bibliothek für Web Vitals und die neuen Informationen aus der Long Animation Frames API (LoAF) verwenden, um Felddaten zu langsamen Interaktionen auf Ihrer Website zu erheben und zu interpretieren.
Mit CrUX die INP Ihrer Website bewerten
Wenn Sie keine Felddaten von den Nutzern Ihrer Website erheben, ist CrUX ein guter Ausgangspunkt. CrUX erfasst Felddaten von echten Chrome-Nutzern, die das Senden von Telemetriedaten aktiviert haben.
CrUX-Daten kommen in einer Reihe verschiedener Bereiche vor und hängen vom Umfang der gesuchten Informationen ab. CrUX kann Daten zu INP und anderen Core Web Vitals für Folgendes bereitstellen:
- Mit PageSpeed Insights können Sie einzelne Seiten und ganze Ursprünge analysieren.
- Seitentypen Viele E-Commerce-Websites haben beispielsweise die Typen „Produktdetailseite“ und „Produktlistenseite“. Sie können die CrUX-Daten für einzelne Seitentypen in der Search Console abrufen.
Als Ausgangspunkt können Sie die URL Ihrer Website in PageSpeed Insights eingeben. Nachdem Sie die URL eingegeben haben, werden Felddaten für diese URL (sofern verfügbar) für mehrere Messwerte angezeigt, einschließlich „In der Nähe von“. Mit den Ein-/Aus-Schaltflächen können Sie auch die INP-Werte für Dimensionen für Mobilgeräte und Computer prüfen.
Diese Daten sind nützlich, da Sie so erkennen können, ob ein Problem vorliegt. CrUX kann Ihnen jedoch nicht sagen, was die Probleme verursacht. Es gibt viele Lösungen für das Echtzeit-Nutzermonitoring (Real User Monitoring, RUM), mit denen Sie eigene Felddaten von den Nutzern Ihrer Website erheben können, um diese Frage zu beantworten. Eine Möglichkeit besteht darin, diese Felddaten selbst mit der JavaScript-Bibliothek für Web Vitals zu erheben.
Felddaten mit der JavaScript-Bibliothek von web-vitals
erfassen
Die web-vitals
-JavaScript-Bibliothek ist ein Script, das Sie auf Ihrer Website laden können, um Felddaten von Ihren Websitenutzern zu erfassen. Sie können damit eine Reihe von Messwerten erfassen, einschließlich INP in Browsern, die diese Funktion unterstützen.
Mit der Standardversion der Web Vitals-Bibliothek können grundlegende INP-Daten von Nutzern vor Ort abgerufen werden:
import {onINP} from 'web-vitals';
onINP(({name, value, rating}) => {
console.log(name); // 'INP'
console.log(value); // 512
console.log(rating); // 'poor'
});
Damit Sie die Felddaten Ihrer Nutzer analysieren können, müssen Sie diese Daten an eine bestimmte Stelle senden:
import {onINP} from 'web-vitals';
onINP(({name, value, rating}) => {
// Prepare JSON to be sent for collection. Note that
// you can add anything else you'd want to collect here:
const body = JSON.stringify({name, value, rating});
// Use `sendBeacon` to send data to an analytics endpoint.
// For Google Analytics, see https://github.com/GoogleChrome/web-vitals#send-the-results-to-google-analytics.
navigator.sendBeacon('/analytics', body);
});
Diese Daten allein sagen Ihnen jedoch nicht viel mehr als CrUX. Hier kommt die Attributionsfunktion der Web Vitals-Bibliothek ins Spiel.
Mehr Möglichkeiten mit der Attributionsfunktion der Web Vitals-Bibliothek
Die Attributionsfunktion der Web Vitals-Bibliothek liefert zusätzliche Daten, die Sie von Nutzern in der Praxis erhalten können. So können Sie Probleme mit Interaktionen, die sich auf die INP Ihrer Website auswirken, besser beheben. Auf diese Daten kann über das attribution
-Objekt zugegriffen werden, das in der onINP()
-Methode der Bibliothek angezeigt wird:
import {onINP} from 'web-vitals/attribution';
onINP(({name, value, rating, attribution}) => {
console.log(name); // 'INP'
console.log(value); // 56
console.log(rating); // 'good'
console.log(attribution); // Attribution data object
});
Neben dem INP der Seite selbst enthält die Attributionsaufschlüsselung viele Daten, mit denen Sie die Gründe für langsame Interaktionen besser nachvollziehen können. So können Sie beispielsweise besser nachvollziehen, auf welchen Teil der Interaktion Sie sich konzentrieren sollten. So lassen sich wichtige Fragen beantworten, darunter:
- „Hat der Nutzer während des Ladevorgangs mit der Seite interagiert?“
- „Haben die Event-Handler der Interaktion lange gedauert?“
- „Wurde der Code des Interaktionsereignis-Handlers verzögert gestartet? Wenn ja, was passierte zu diesem Zeitpunkt sonst noch im Hauptthread?“
- „Hat die Interaktion viel Renderingaufwand verursacht, wodurch das Rendern des nächsten Frames verzögert wurde?“
In der folgenden Tabelle sind einige der grundlegenden Attributionsdaten aufgeführt, die Sie in der Bibliothek abrufen können. Anhand dieser Daten können Sie die Ursachen für langsame Interaktionen auf Ihrer Website grob ermitteln:
attribution -Objektschlüssel
|
Daten |
---|---|
interactionTarget
|
Ein CSS-Selektor, der auf das Element verweist, das den INP-Wert der Seite generiert hat, z. B. button#save .
|
interactionType
|
Der Interaktionstyp, z. B. Klicks, Tippen oder Tastatureingaben. |
inputDelay *
|
Die Eingabeverzögerung der Interaktion. |
processingDuration *
|
Die Zeitspanne zwischen dem Start des ersten Ereignislisteners als Reaktion auf die Nutzerinteraktion und dem Abschluss der Verarbeitung aller Ereignislistener. |
presentationDelay *
|
Die Darstellungsverzögerung der Interaktion, die vom Ende der Ereignishandler bis zum Zeichnen des nächsten Frames vergeht. |
longAnimationFrameEntries *
|
Einträge aus dem LoAF, die mit der Interaktion verknüpft sind. Weitere Informationen finden Sie im nächsten Abschnitt. |
Ab Version 4 der Web Vitals-Bibliothek erhalten Sie über die von der Bibliothek bereitgestellten Daten mit INP-Phasenaufschlüsselungen (Eingabeverzögerung, Verarbeitungsdauer und Präsentationsverzögerung) und der Long Animation Frames API (LoAF) noch tiefere Einblicke in problematische Interaktionen.
Long Animation Frames API (LoAF)
Das Beheben von Interaktionsproblemen mithilfe von Felddaten ist eine Herausforderung. Mit Daten aus LoAF können Sie jedoch jetzt besser nachvollziehen, was die Ursache für langsame Interaktionen ist. LoAF bietet eine Reihe detaillierter Zeitangaben und anderer Daten, mit denen Sie die genauen Ursachen ermitteln können – und vor allem, wo sich die Ursache des Problems im Code Ihrer Website befindet.
Die Attributionsversion der Web Vitals-Bibliothek stellt unter dem Schlüssel longAnimationFrameEntries
des attribution
-Objekts ein Array von LoAF-Einträgen bereit. In der folgenden Tabelle sind einige wichtige Daten aufgeführt, die Sie in jedem LoAF-Eintrag finden:
LoAF-Eintragsobjektschlüssel | Daten |
---|---|
duration
|
Die Dauer des langen Animationsframes bis zur Fertigstellung des Layouts, mit Ausnahme von Painting und Compositing. |
blockingDuration
|
Die Gesamtzeit im Frame, in der der Browser aufgrund langer Aufgaben nicht schnell reagieren konnte. Diese Blockierungszeit kann lange Aufgaben umfassen, bei denen JavaScript ausgeführt wird, sowie alle nachfolgenden langen Rendering-Aufgaben im Frame. |
firstUIEventTimestamp
|
Der Zeitstempel, zu dem das Ereignis während des Frames in die Warteschlange gestellt wurde. Nützlich, um den Beginn der Eingabeverzögerung einer Interaktion zu ermitteln. |
startTime
|
Der Startzeitstempel des Frames. |
renderStart
|
Wann das Rendering des Frames begonnen hat. Dazu gehören alle requestAnimationFrame -Callbacks (und gegebenenfalls ResizeObserver -Callbacks), aber möglicherweise vor Beginn der Stil-/Layoutarbeiten.
|
styleAndLayoutStart
|
Wenn Stil-/Layoutänderungen im Frame vorgenommen werden. Dies kann hilfreich sein, um die Länge des Stils/Layouts zu ermitteln, wenn Sie andere verfügbare Zeitstempel ermitteln. |
scripts
|
Ein Array mit Elementen mit Informationen zur Skriptzuordnung, die zum INP der Seite beitragen. |
Anhand dieser Informationen können Sie viel darüber erfahren, was eine Interaktion verlangsamt. Das scripts
-Array, das in LoAF-Einträgen angezeigt wird, sollte jedoch von besonderem Interesse sein:
Schlüssel des Script-Attributionsobjekts | Daten |
---|---|
invoker
|
Entspricht dem Aufrufer. Dies kann je nach Aufruftyp, der in der nächsten Zeile beschrieben wird, variieren. Beispiele für Auslöser sind Werte wie 'IMG#id.onload' , 'Window.requestAnimationFrame' oder 'Response.json.then' . |
invokerType
|
Der Typ des Aufrufers. Kann 'user-callback' , 'event-listener' , 'resolve-promise' , 'reject-promise' , 'classic-script' oder 'module-script' sein.
|
sourceURL
|
Die URL zum Script, aus dem der lange Animationsframe stammt. |
sourceCharPosition
|
Die Zeichenposition im Script, die durch sourceURL gekennzeichnet ist.
|
sourceFunctionName
|
Der Name der Funktion im identifizierten Skript. |
Jeder Eintrag in diesem Array enthält die in dieser Tabelle aufgeführten Daten. Sie enthalten Informationen zum Script, das für die langsame Interaktion verantwortlich war, und dazu, wie es dazu beigetragen hat.
Häufige Ursachen für langsame Interaktionen messen und identifizieren
In diesem Leitfaden erfahren Sie, wie Sie die in der web-vitals
-Bibliothek angezeigten LoAF-Daten nutzen können, um einige Ursachen für langsame Interaktionen zu ermitteln.
Lange Verarbeitungsdauer
Die Verarbeitungsdauer einer Interaktion ist die Zeit, die benötigt wird, bis die registrierten Event-Handler-Callbacks der Interaktion vollständig ausgeführt werden, sowie alles andere, was dazwischen passiert. Die Web Vitals-Bibliothek zeigt lange Verarbeitungszeiten an:
import {onINP} from 'web-vitals/attribution';
onINP(({name, value, attribution}) => {
const {processingDuration} = attribution; // 512.5
});
Es ist ganz normal, anzunehmen, dass die Hauptursache für eine langsame Interaktion darin besteht, dass die Ausführung Ihres Ereignis-Handler-Codes zu lange gedauert hat. Dies ist jedoch nicht immer der Fall. Sobald Sie bestätigt haben, dass dies das Problem ist, können Sie mit LoAF-Daten genauer nachsehen:
import {onINP} from 'web-vitals/attribution';
onINP(({name, value, attribution}) => {
const {processingDuration} = attribution; // 512.5
// Get the longest script from LoAF covering `processingDuration`:
const loaf = attribution.longAnimationFrameEntries.at(-1);
const script = loaf?.scripts.sort((a, b) => b.duration - a.duration)[0];
if (script) {
// Get attribution for the long-running event handler:
const {invokerType} = script; // 'event-listener'
const {invoker} = script; // 'BUTTON#update.onclick'
const {sourceURL} = script; // 'https://example.com/app.js'
const {sourceCharPosition} = script; // 83
const {sourceFunctionName} = script; // 'update'
}
});
Wie Sie im vorherigen Code-Snippet sehen können, können Sie mit LoAF-Daten die genaue Ursache für eine Interaktion mit einer langen Verarbeitungsdauer ermitteln, z. B.:
- Das Element und sein registrierter Ereignis-Listener.
- Die Scriptdatei und die Zeichenposition darin, die den Code des lang laufenden Ereignis-Handlers enthält.
- Der Name der Funktion.
Diese Art von Daten ist von unschätzbarem Wert. Sie müssen nicht mehr mühsam herausfinden, welche Interaktion oder welcher Ereignishandler für lange Verarbeitungsdauern verantwortlich ist. Da Drittanbieter-Scripts oft eigene Ereignishandler registrieren können, können Sie auch feststellen, ob Ihr Code dafür verantwortlich ist. Wenn Sie die Kontrolle über den Code haben, sollten Sie sich mit der Optimierung langer Aufgaben befassen.
Lange Eingabeverzögerungen
Langlaufende Event-Handler sind zwar üblich, aber es gibt noch andere Aspekte der Interaktion, die Sie berücksichtigen sollten. Ein Teil tritt vor der Verarbeitungsdauer auf und wird als Eingabeverzögerung bezeichnet. Das ist die Zeitspanne zwischen dem Starten der Interaktion durch den Nutzer und dem Ausführen der Rückrufe des Ereignishandlers. Dieser Fall tritt auf, wenn der Hauptthread bereits eine andere Aufgabe verarbeitet. Mit der Attributionsfunktion der Web Vitals-Bibliothek können Sie die Länge der Eingabeverzögerung für eine Interaktion ermitteln:
import {onINP} from 'web-vitals/attribution';
onINP(({name, value, attribution}) => {
const {inputDelay} = attribution; // 125.59439536
});
Wenn Sie feststellen, dass einige Interaktionen lange Eingabeverzögerungen haben, müssen Sie herausfinden, was zum Zeitpunkt der Interaktion auf der Seite passiert ist, was zu der langen Eingabeverzögerung geführt hat. Das hängt oft davon ab, ob die Interaktion während des Ladens der Seite oder danach stattgefunden hat.
Tritt das Problem beim Laden der Seite auf?
Der Hauptthread ist oft am stärksten besucht, wenn eine Seite geladen wird. Während dieser Zeit werden alle möglichen Aufgaben in die Warteschlange gestellt und verarbeitet. Wenn der Nutzer versucht, währenddessen mit der Seite zu interagieren, kann dies die Interaktion verzögern. Seiten, auf denen viel JavaScript geladen wird, können die Kompilierung und Auswertung von Scripts sowie die Ausführung von Funktionen auslösen, die eine Seite für Nutzerinteraktionen vorbereiten. Dies kann ein Problem für die Nutzenden haben, wenn diese Aktivität stattfindet, und Sie können herausfinden, ob das auf die Nutzenden Ihrer Website zutrifft:
import {onINP} from 'web-vitals/attribution';
onINP(({name, value, attribution}) => {
const {inputDelay} = attribution; // 125.59439536
// Get the longest script from the first LoAF entry:
const loaf = attribution.longAnimationFrameEntries[0];
const script = loaf?.scripts.sort((a, b) => b.duration - a.duration)[0];
if (script) {
// Invoker types can describe if script eval blocked the main thread:
const {invokerType} = script; // 'classic-script' | 'module-script'
const {sourceLocation} = script; // 'https://example.com/app.js'
}
});
Wenn Sie diese Daten vor Ort erfassen und Sie hohe Eingabeverzögerungen und Aufrufer-Typen wie 'classic-script'
oder 'module-script'
sehen, ist es mit Ordnung zu sagen, dass die Auswertung von Skripts auf Ihrer Website sehr lange dauert und den Hauptthread so lange blockieren, dass Interaktionen verzögert werden. Sie können diese Blockierungszeit reduzieren, indem Sie Ihre Skripts in kleinere Bundles aufteilen, anfangs nicht verwendeten Code aufschieben und zu einem späteren Zeitpunkt laden. Außerdem können Sie Ihre Website auf nicht verwendeten Code prüfen, den Sie vollständig entfernen können.
War es nach dem Laden der Seite?
Eingabeverzögerungen treten zwar häufig beim Laden einer Seite auf, aber es ist genauso möglich, dass sie nach dem Laden einer Seite auftreten, und zwar aus einer ganz anderen Ursache. Häufige Ursachen für Eingabeverzögerungen nach dem Laden der Seite sind Code, der aufgrund eines früheren setInterval
-Aufrufs regelmäßig ausgeführt wird, oder sogar Ereignis-Callbacks, die für eine frühere Ausführung in die Warteschlange gestellt wurden und noch verarbeitet werden.
import {onINP} from 'web-vitals/attribution';
onINP(({name, value, attribution}) => {
const {inputDelay} = attribution; // 125.59439536
// Get the longest script from the first LoAF entry:
const loaf = attribution.longAnimationFrameEntries[0];
const script = loaf?.scripts.sort((a, b) => b.duration - a.duration)[0];
if (script) {
const {invokerType} = script; // 'user-callback'
const {sourceURL} = script; // 'https://example.com/app.js'
const {sourceCharPosition} = script; // 83
const {sourceFunctionName} = script; // 'update'
}
});
Wie bei der Fehlerbehebung für hohe Werte für die Verarbeitungsdauer erhalten Sie durch hohe Eingabeverzögerungen aufgrund der oben genannten Ursachen detaillierte Skriptzuordnungsdaten. Der Aufrufertyp ändert sich jedoch je nach Art der Arbeit, die die Interaktion verzögert hat:
'user-callback'
gibt an, dass die blockierende Aufgabe vonsetInterval
,setTimeout
oder sogarrequestAnimationFrame
stammte.'event-listener'
gibt an, dass die blockierende Aufgabe von einer früheren Eingabe stammt, die in die Warteschlange gestellt wurde und noch verarbeitet wird.'resolve-promise'
und'reject-promise'
bedeuten, dass die blockierende Aufgabe aus einer asynchronen Arbeit stammt, die zuvor gestartet wurde und zu einem Zeitpunkt abgeschlossen oder abgelehnt wurde, als der Nutzer versuchte, mit der Seite zu interagieren, was die Interaktion verzögerte.
Die Daten zur Scriptzuordnung geben Ihnen in jedem Fall einen Hinweis darauf, wo Sie suchen müssen und ob die Eingabeverzögerung auf Ihren eigenen Code oder den eines Drittanbieter-Scripts zurückzuführen ist.
Lange Verzögerungen bei der Präsentation
Präsentationsverzögerungen sind die letzte Phase einer Interaktion. Sie beginnen, wenn die Ereignishandler der Interaktion abgeschlossen sind, und enden, wenn der nächste Frame gerendert wurde. Sie treten auf, wenn die Arbeit in einem Ereignishandler aufgrund einer Interaktion den visuellen Zustand der Benutzeroberfläche ändert. Wie bei Verarbeitungsdauern und Eingabeverzögerungen können Sie mit der Web Vitals-Bibliothek auch ermitteln, wie lang die Präsentationsverzögerung für eine Interaktion war:
import {onINP} from 'web-vitals/attribution';
onINP(({name, value, attribution}) => {
const {presentationDelay} = attribution; // 113.32307691
});
Wenn Sie diese Daten erfassen und hohe Präsentationsverzögerungen für Interaktionen feststellen, die zum INP Ihrer Website beitragen, kann das verschiedene Ursachen haben. Im Folgenden finden Sie einige mögliche Gründe.
Teure Stil- und Layoutarbeiten
Lange Präsentationsverzögerungen können auf kostspielige Stilneuberechnungen und Layoutarbeiten zurückzuführen sein, die aus einer Reihe von Gründen entstehen, einschließlich komplexer CSS-Selektoren und großer DOM-Größen. Sie können die Dauer dieser Arbeit mit den LoAF-Zeiten messen, die in der Web Vitals-Bibliothek angezeigt werden:
import {onINP} from 'web-vitals/attribution';
onINP(({name, value, attribution}) => {
const {presentationDelay} = attribution; // 113.32307691
// Get the longest script from the last LoAF entry:
const loaf = attribution.longAnimationFrameEntries.at(-1);
const script = loaf?.scripts.sort((a, b) => b.duration - a.duration)[0];
// Get necessary timings:
const {startTime} = loaf; // 2120.5
const {duration} = loaf; // 1002
// Figure out the ending timestamp of the frame (approximate):
const endTime = startTime + duration; // 3122.5
// Get the start timestamp of the frame's style/layout work:
const {styleAndLayoutStart} = loaf; // 3011.17692309
// Calculate the total style/layout duration:
const styleLayoutDuration = endTime - styleAndLayoutStart; // 111.32307691
if (script) {
// Get attribution for the event handler that triggered
// the long-running style and layout operation:
const {invokerType} = script; // 'event-listener'
const {invoker} = script; // 'BUTTON#update.onclick'
const {sourceURL} = script; // 'https://example.com/app.js'
const {sourceCharPosition} = script; // 83
const {sourceFunctionName} = script; // 'update'
}
});
LoAF gibt nicht an, wie lange die Stil- und Layoutarbeit für einen Frame gedauert hat, aber es wird angezeigt, wann sie begonnen hat. Anhand dieses Startzeitstempels können Sie mit anderen Daten aus LoAF die genaue Dauer dieser Arbeit berechnen, indem Sie die Endzeit des Frames ermitteln und davon den Startzeitstempel der Stil- und Layoutarbeit abziehen.
Lang andauernde requestAnimationFrame
-Callbacks
Eine mögliche Ursache für lange Präsentationsverzögerungen ist zu viel Arbeit in einem requestAnimationFrame
-Callback. Der Inhalt dieses Callbacks wird ausgeführt, nachdem die Ereignis-Handler ausgeführt wurden, aber unmittelbar vor der Neuberechnung von Stil und Layout.
Die Ausführung dieser Callbacks kann viel Zeit in Anspruch nehmen, wenn die darin ausgeführte Arbeit komplex ist. Wenn Sie vermuten, dass hohe Werte für die Präsentationsverzögerung auf die Arbeit mit requestAnimationFrame
zurückzuführen sind, können Sie die LoAF-Daten der Web Vitals-Bibliothek zur Identifizierung folgender Szenarien verwenden:
onINP(({name, value, attribution}) => {
const {presentationDelay} = attribution; // 543.1999999880791
// Get the longest script from the last LoAF entry:
const loaf = attribution.longAnimationFrameEntries.at(-1);
const script = loaf?.scripts.sort((a, b) => b.duration - a.duration)[0];
// Get the render start time and when style and layout began:
const {renderStart} = loaf; // 2489
const {styleAndLayoutStart} = loaf; // 2989.5999999940395
// Calculate the `requestAnimationFrame` callback's duration:
const rafDuration = styleAndLayoutStart - renderStart; // 500.59999999403954
if (script) {
// Get attribution for the event handler that triggered
// the long-running requestAnimationFrame callback:
const {invokerType} = script; // 'user-callback'
const {invoker} = script; // 'FrameRequestCallback'
const {sourceURL} = script; // 'https://example.com/app.js'
const {sourceCharPosition} = script; // 83
const {sourceFunctionName} = script; // 'update'
}
});
Wenn ein erheblicher Teil der Präsentationsverzögerung auf einen requestAnimationFrame
-Callback zurückzuführen ist, sollten Sie in diesen Callbacks nur Aufgaben ausführen, die zu einer tatsächlichen Aktualisierung der Benutzeroberfläche führen. Alle anderen Arbeiten, die das DOM nicht betreffen oder Stile nicht aktualisieren, verzögern unnötig das Zeichnen des nächsten Frames. Seien Sie also vorsichtig!
Fazit
Felddaten sind die beste Informationsquelle, um zu verstehen, welche Interaktionen für die Nutzer vor Ort problematisch sind. Wenn Sie Tools zur Datenerfassung für Felder wie die JavaScript-Bibliothek „Web-Vitals“ (oder einen RUM-Anbieter) verwenden, können Sie besser einschätzen, welche Interaktionen am problematischsten sind. Sie können dann problematische Interaktionen im Lab reproduzieren und dann an ihrer Behebung arbeiten.
Hero-Image von Unsplash von Federico Respini