Interaktion für den nächsten Farbauftrag optimieren

Hier erfahren Sie, wie Sie die Interaction to Next Paint für Ihre Website optimieren.

Interaction to Next Paint (INP) ist ein stabiler Core Web Vitals-Messwert, der die allgemeine Reaktionsfähigkeit einer Seite auf Nutzerinteraktionen bewertet. Dazu wird die Latenz aller qualifizierenden Interaktionen erfasst, die während des Besuchs eines Nutzers auf einer Seite stattfinden. Der endgültige INP-Wert ist die längste beobachtete Interaktion. Ausreißer werden manchmal ignoriert.

Für eine gute Nutzererfahrung sollten Websites eine Interaction to Next Paint von maximal 200 Millisekunden anstreben. Damit Sie dieses Ziel für die meisten Nutzer erreichen, sollten Sie das 75. Perzentil der Seitenaufrufe, aufgeschlüsselt nach Mobilgeräten und Desktop-Geräten, messen.

Gute INP-Werte sind 200 Millisekunden oder weniger, schlechte Werte über 500 Millisekunden und alles dazwischen muss verbessert werden.

Je nach Website gibt es möglicherweise nur wenige oder gar keine Interaktionen, z. B. Seiten, die hauptsächlich Text und Bilder mit wenigen oder gar keinen interaktiven Elementen enthalten. Oder bei Websites wie Texteditoren oder Spielen könnten es Hunderte – wenn nicht sogar Tausende – von Interaktionen geben. In beiden Fällen ist die Nutzererfahrung bei einem hohen INP gefährdet.

Es braucht Zeit und Mühe, INP zu verbessern, aber die Belohnung ist die Nutzerfreundlichkeit. In diesem Leitfaden wird ein Weg zur Verbesserung von INP erläutert.

Ursache für schlechte INP-Werte ermitteln

Bevor Sie das Problem mit langsamen Interaktionen beheben können, benötigen Sie Daten, die Aufschluss darüber geben, ob die INP Ihrer Website schlecht ist oder verbessert werden muss. Sobald Sie diese Informationen haben, können Sie in das Labor gehen, langsame Interaktionen diagnostizieren und sich auf eine Lösung hinarbeiten.

Langsame Interaktionen vor Ort erkennen

Idealerweise beginnen Sie bei der Optimierung von INP mit Felddaten. Im Idealfall liefern Felddaten von einem RUM-Anbieter (Real User Monitoring) nicht nur den INP-Wert einer Seite, sondern auch Kontextdaten, die zeigen, welche spezifische Interaktion für den INP-Wert selbst verantwortlich war, ob die Interaktion während oder nach dem Seitenaufbau erfolgte, den Typ der Interaktion (Klicken, Tastendruck oder Tippen) und andere wertvolle Informationen.

Wenn Sie sich zum Abrufen von Felddaten nicht auf einen RUM-Anbieter verlassen, wird im INP-Felddatenleitfaden empfohlen, den Bericht zur Nutzererfahrung in Chrome (Chrome User Experience, CrUX) über PageSpeed Insights zu verwenden, um die Lücken zu schließen. CrUX ist das offizielle Dataset des Core Web Vitals-Programms und bietet eine grobe Zusammenfassung der Messwerte für Millionen von Websites, einschließlich INP. Allerdings stellt CrUX häufig nicht die Kontextdaten bereit, die Sie von einem RUM-Anbieter erhalten würden, um Ihnen bei der Analyse von Problemen zu helfen. Daher empfehlen wir weiterhin, nach Möglichkeit einen RUM-Anbieter zu verwenden oder eine eigene RUM-Lösung zu implementieren, um die in Chrome UX verfügbaren Funktionen zu ergänzen.

Langsame Interaktionen im Labor diagnostizieren

Idealerweise sollten Sie mit den Tests im Labor beginnen, sobald Ihnen Felddaten vorliegen, die auf langsame Interaktionen hindeuten. Wenn keine Felddaten vorhanden sind, gibt es einige Strategien, um langsame Interaktionen im Labor zu erkennen. Zu diesen Strategien gehören das Verfolgen gängiger Aufrufabfolgen und das Testen von Interaktionen während des Ladevorgangs sowie die Interaktion mit der Seite während des Ladevorgangs – wenn der Hauptthread oft am stärksten frequentiert ist –, um langsame Interaktionen während dieses entscheidenden Teils der User Experience zu erkennen.

Interaktionen optimieren

Sobald Sie eine langsame Interaktion gefunden haben und sie manuell im Lab reproduzieren können, besteht der nächste Schritt darin, sie zu optimieren. Interaktionen lassen sich in drei Phasen unterteilen:

  1. Die Eingabeverzögerung, die beginnt, wenn der Nutzer eine Interaktion mit der Seite initiiert, und endet, wenn die Ereignis-Callbacks für die Interaktion beginnen.
  2. Die Verarbeitungszeit, die aus der Zeit besteht, die für die Ausführung von Ereignis-Callbacks benötigt wird.
  3. Die Präsentationsverzögerung, also die Zeit, die der Browser benötigt, um den nächsten Frame mit dem visuellen Ergebnis der Interaktion anzuzeigen.

Die Summe dieser drei Phasen ergibt die gesamte Interaktionslatenz. Jede einzelne Phase einer Interaktion trägt eine gewisse Zeit zur Gesamtinteraktionslatenz bei. Daher ist es wichtig zu wissen, wie Sie jeden Teil der Interaktion optimieren können, damit sie so wenig Zeit wie möglich ausgeführt wird.

Eingabeverzögerung erkennen und reduzieren

Wenn ein Nutzer mit einer Seite interagiert, ist der erste Teil der Interaktion die Eingabeverzögerung. Je nach anderen Aktivitäten auf der Seite können Eingabeverzögerungen beträchtlich sein. Dies kann durch Aktivitäten im Hauptthread (z. B. durch Laden, Parsen und Kompilieren von Skripts), Abrufverarbeitung, Timerfunktionen oder sogar durch andere Interaktionen verursacht werden, die schnell hintereinander stattfinden und sich miteinander überschneiden.

Unabhängig von der Ursache der Eingabeverzögerung einer Interaktion sollten Sie die Eingabeverzögerung auf ein Minimum reduzieren, damit Interaktionen so schnell wie möglich Ereignis-Callbacks ausführen können.

Die Beziehung zwischen Skriptauswertung und langen Aufgaben beim Start

Ein wichtiger Aspekt der Interaktivität im Lebenszyklus einer Seite ist der Start. Beim Laden einer Seite wird sie zuerst gerendert. Wenn eine Seite gerendert wurde, bedeutet das aber nicht, dass sie vollständig geladen wurde. Je nachdem, wie viele Ressourcen eine Seite benötigt, um voll funktionsfähig zu sein, kann es vorkommen, dass Nutzer versuchen, mit der Seite zu interagieren, während sie noch geladen wird.

Eine Sache, die die Eingabeverzögerung einer Interaktion während des Ladens einer Seite verlängern kann, ist die Skriptauswertung. Nachdem eine JavaScript-Datei aus dem Netzwerk abgerufen wurde, muss der Browser noch einige Schritte ausführen, bevor JavaScript ausgeführt werden kann. Dazu gehört das Parsen eines Skripts, um sicherzustellen, dass seine Syntax gültig ist, das Kompilieren in Bytecode und die anschließende Ausführung des Skripts.

Abhängig von der Größe eines Skripts können dadurch lange Aufgaben im Hauptthread erforderlich sein und der Browser reagiert möglicherweise nicht mehr auf andere Nutzerinteraktionen. Damit Ihre Seite während des Seitenaufbaus auf Nutzereingaben reagieren kann, sollten Sie wissen, wie Sie die Wahrscheinlichkeit langer Aufgaben während des Seitenaufbaus reduzieren können, damit die Seite schnell bleibt.

Ereignis-Callbacks optimieren

Die Eingabeverzögerung ist nur der erste Teil dessen, was INP misst. Außerdem müssen die Ereignis-Callbacks, die als Reaktion auf eine Nutzerinteraktion ausgeführt werden, so schnell wie möglich abgeschlossen werden.

Gibt dem Hauptthread häufig neue Ergebnisse

Der beste allgemeine Rat bei der Optimierung von Ereignis-Callbacks besteht darin, so wenig Arbeit wie möglich damit zu machen. Ihre Interaktionslogik kann jedoch komplex sein und Sie können den Arbeitsaufwand dieser Person möglicherweise nur geringfügig reduzieren.

Wenn Sie feststellen, dass dies auf Ihre Website zutrifft, können Sie als Nächstes die Arbeit in Ereignis-Callbacks in separate Aufgaben aufteilen. Dadurch wird verhindert, dass aus der gemeinsamen Arbeit eine lange Aufgabe wird, die den Hauptthread blockiert. Dadurch können andere Interaktionen, die sonst auf den Hauptthread warten würden, früher ausgeführt werden.

setTimeout ist eine Möglichkeit, Aufgaben aufzuteilen, da der an sie übergebene Callback in einer neuen Aufgabe ausgeführt wird. Sie können setTimeout alleine verwenden oder die Verwendung in einer separaten Funktion abstrahieren, um einen ergonomischeren Ertrag zu erzielen.

Undifferenziertes Ergebnis ist besser als gar nicht. Es gibt jedoch eine differenziertere Methode, um im Hauptthread etwas nachzugeben, und zwar nur direkt nach einem Ereignis-Callback, der die Benutzeroberfläche aktualisiert, sodass die Rendering-Logik früher ausgeführt werden kann.

Ergibt nach, damit das Rendering früher durchgeführt werden kann

Eine fortgeschrittenere Technik beinhaltet die Strukturierung des Codes in Ihren Ereignis-Callbacks, um die Ausführung auf die Logik zu beschränken, die für die Anwendung visueller Aktualisierungen für den nächsten Frame erforderlich ist. Alles andere kann auf eine nachfolgende Aufgabe übertragen werden. Dies sorgt nicht nur dafür, dass Callbacks leicht und flink sind, sondern auch die Renderingzeit für Interaktionen verbessert, da keine visuellen Aktualisierungen im Ereignis-Callback-Code blockiert werden.

Stellen Sie sich beispielsweise einen Rich-Text-Editor vor, der Text während der Eingabe formatiert, aber auch andere Aspekte der Benutzeroberfläche entsprechend Ihrem Text aktualisiert (z. B. Wortzahl, Hervorhebung von Rechtschreibfehlern und anderes wichtiges visuelles Feedback). Außerdem muss die Anwendung möglicherweise auch speichern, was Sie geschrieben haben, damit Ihnen keine Arbeit verloren geht, falls Sie die Anwendung verlassen und zurückkehren.

In diesem Beispiel müssen die folgenden vier Dinge als Reaktion auf vom Nutzer eingegebene Zeichen ausgeführt werden. Allerdings muss nur das erste Element erledigt werden, bevor der nächste Frame angezeigt wird.

  1. Aktualisieren Sie das Textfeld mit der Eingabe des Nutzers und wenden Sie alle erforderlichen Formatierungen an.
  2. Aktualisieren Sie den Teil der Benutzeroberfläche, in dem die aktuelle Wortzahl angezeigt wird.
  3. Führen Sie Logik zur Überprüfung auf Rechtschreibfehler aus.
  4. Speichern Sie die letzten Änderungen (entweder lokal oder in einer Remote-Datenbank).

Der entsprechende Code könnte etwa so aussehen:

textBox.addEventListener('input', (inputEvent) => {
  // Update the UI immediately, so the changes the user made
  // are visible as soon as the next frame is presented.
  updateTextBox(inputEvent);

  // Use `setTimeout` to defer all other work until at least the next
  // frame by queuing a task in a `requestAnimationFrame()` callback.
  requestAnimationFrame(() => {
    setTimeout(() => {
      const text = textBox.textContent;
      updateWordCount(text);
      checkSpelling(text);
      saveChanges(text);
    }, 0);
  });
});

Die folgende Visualisierung zeigt, wie das Aufschieben nicht kritischer Updates bis nach dem nächsten Frame die Verarbeitungszeit und damit die Gesamtinteraktionslatenz reduzieren kann.

Darstellung einer Tastaturinteraktion und weiterer Aufgaben in zwei Szenarien. In der oberen Abbildung werden die Rendering-kritische Aufgabe und alle nachfolgenden Hintergrundaufgaben synchron ausgeführt, bis die Möglichkeit zur Darstellung eines Frames eingetroffen ist. In der unteren Abbildung wird die Rendering-kritische Arbeit zuerst ausgeführt und dann an den Hauptthread übergeben, um schneller einen neuen Frame zu präsentieren. Anschließend werden die Hintergrundaufgaben ausgeführt.
Klicken Sie auf die Abbildung oben, um sich eine hochauflösende Version anzusehen.

Die Verwendung von setTimeout() innerhalb eines requestAnimationFrame()-Aufrufs im vorherigen Codebeispiel ist zugegebenermaßen esoterisch, aber sie ist eine effektive Methode, die in allen Browsern funktioniert und so dafür sorgt, dass der nicht kritische Code den nächsten Frame nicht blockiert.

Seitenübergreifendes Layout vermeiden

Layout-Überlastung – manchmal auch als erzwungenes synchrones Layout bezeichnet – ist ein Problem mit der Rendering-Leistung, bei dem das Layout synchron erfolgt. Sie wird angezeigt, wenn Sie Stile in JavaScript aktualisieren und sie dann in derselben Aufgabe lesen. Es gibt viele Eigenschaften in JavaScript, die zu Layoutproblemen führen können.

Visualisierung von Layout-Überschlägen, wie im Leistungsbereich der Chrome-Entwicklertools zu sehen
Ein Beispiel für Layout-Thrashing, wie im Leistungsbereich der Chrome-Entwicklertools zu sehen ist. Renderingaufgaben, bei denen Layout-Maßnahmen durchgeführt werden, werden mit einem roten Dreieck in der oberen rechten Ecke des Aufrufstacks gekennzeichnet, das häufig mit Stil neu berechnen oder Layout beschriftet ist.

Layout-Überschreitungen stellen einen Leistungsengpass dar, da der Browser durch das Aktualisieren von Stilen und anschließendes sofortiges Anfordern der Werte dieser Stile in JavaScript zu synchronen Layoutarbeiten gezwungen ist, andernfalls hätte er später mit der asynchronen Ausführung warten können, nachdem die Ereignisrückrufe beendet wurden.

Präsentationsverzögerung minimieren

Die Darstellungsverzögerung von Interaktionsmarkierungen reicht vom Ende der Ausführung der Ereignis-Callbacks einer Interaktion bis zu dem Zeitpunkt, an dem der Browser den nächsten Frame zeichnen kann, der die resultierenden visuellen Änderungen zeigt.

DOM-Größe minimieren

Wenn das DOM einer Seite klein ist, wird das Rendern in der Regel schnell abgeschlossen. Wenn die DOMs jedoch sehr groß werden, wird die Rendering-Arbeit tendenziell mit zunehmender DOM-Größe skaliert. Die Beziehung zwischen Rendering-Arbeit und DOM-Größe ist nicht linear, aber große DOMs erfordern mehr Arbeit als kleine DOMs. Ein großes DOM ist in zwei Fällen problematisch:

  1. Während des ersten Seiten-Renderings, bei dem ein großes DOM viel Arbeit erfordert, um den Anfangszustand der Seite zu rendern.
  2. Als Reaktion auf eine Nutzerinteraktion, bei der ein großes DOM dazu führen kann, dass Rendering-Aktualisierungen sehr teuer sind, wodurch die Zeit verlängert wird, die der Browser benötigt, um den nächsten Frame darzustellen.

Es gibt Fälle, in denen große DOMs nicht signifikant reduziert werden können. Es gibt zwar Ansätze, mit denen Sie die DOM-Größe reduzieren können, z. B. DOM vereinfachen oder DOM während Nutzerinteraktionen zum DOM hinzufügen, um die ursprüngliche DOM-Größe klein zu halten, diese Verfahren sind jedoch möglicherweise nur bis zu einem bestimmten Punkt wirksam.

Verwende content-visibility, um Elemente außerhalb des Bildschirms verzögert zu rendern

Eine Möglichkeit, den Arbeitsaufwand beim Seitenaufbau und beim Rendern in Reaktion auf Nutzerinteraktionen zu begrenzen, besteht darin, auf die CSS-Eigenschaft content-visibility zu stützen. Dabei handelt es sich im Grunde um ein verzögertes Rendering von Elementen, wenn sie sich dem Darstellungsbereich nähern. Die effektive Nutzung von content-visibility erfordert zwar etwas Übung, es lohnt sich aber zu untersuchen, ob sich dadurch die Renderingzeit Ihrer Seite verbessern lässt.

Beachten Sie die Leistungskosten beim Rendern von HTML mit JavaScript.

Wenn HTML vorhanden ist, findet auch HTML-Parsing statt. Wenn der Browser HTML in ein DOM geparst hat, muss er Stile darauf anwenden, Layoutberechnungen durchführen und dieses Layout anschließend rendern. Dies ist unvermeidlich, aber es kommt auf die wie beim Rendern von HTML an.

Wenn der Server HTML sendet, kommt dieser als Stream im Browser an. Streaming bedeutet, dass die HTML-Antwort vom Server in Blöcken eingeht. Der Browser optimiert die Verarbeitung eines Streams, indem er Teile dieses Streams inkrementell parst, wenn sie ankommen, und sie Schritt für Schritt rendern. Hierbei handelt es sich um eine Leistungsoptimierung, da der Browser beim Laden der Seite regelmäßig und automatisch aktiv wird, und Sie erhalten diesen kostenlos.

Der erste Besuch einer Website umfasst zwar immer etwas HTML-Code, aber ein üblicher Ansatz beginnt mit einem minimalen HTML-Code, woraufhin JavaScript eingesetzt wird, um den Inhaltsbereich zu füllen. Auch spätere Aktualisierungen dieses Inhaltsbereichs sind das Ergebnis von Nutzerinteraktionen. Dies wird normalerweise als SPA-Modell (Single-Page Application) bezeichnet. Ein Nachteil dieses Musters besteht darin, dass beim Rendern von HTML mit JavaScript auf dem Client nicht nur die Kosten für die JavaScript-Verarbeitung zum Erstellen des HTML-Codes anfallen, sondern dass der Browser auch dann erst Ergebnisse liefert, wenn er den HTML-Code vollständig geparst und gerendert hat.

Beachten Sie jedoch, dass auch Websites, die keine SPAs sind, aufgrund von Interaktionen in gewissem Umfang HTML-Rendering über JavaScript erfordern. Dies ist im Allgemeinen in Ordnung, solange Sie keine großen HTML-Mengen auf dem Client rendern, wodurch die Darstellung des nächsten Frames verzögert werden kann. Es ist jedoch wichtig, die Leistungsauswirkungen dieses Ansatzes bei der Darstellung von HTML im Browser zu verstehen und zu verstehen, wie sich dieser auf die Reaktionsfähigkeit Ihrer Website auf Nutzereingaben auswirken kann, wenn Sie viel HTML-Code über JavaScript rendern.

Fazit

Die Verbesserung des INP auf Ihrer Website ist ein iterativer Prozess. Wenn Sie eine langsame Interaktion vor Ort beheben, ist die Wahrscheinlichkeit hoch, dass Sie andere langsame Interaktionen finden, die Sie auch optimieren müssen – insbesondere, wenn Ihre Website viel Interaktivität bietet.

Der Schlüssel zur Verbesserung des INP ist Beharrlichkeit. Mit der Zeit können Sie erreichen, dass Ihre Seite auf einen Bereich reagiert, an dem Nutzer mit Ihrer Erfahrung zufrieden sind. Es ist auch gut möglich, dass Sie bei der Entwicklung neuer Funktionen für Ihre Nutzer möglicherweise denselben Prozess zur Optimierung spezifischer Interaktionen für Ihre Nutzer durchlaufen müssen. Es wird Zeit und Mühe erfordern, aber es ist gut investiert.

Hero-Image von Unsplash von David Pisnoy, geändert gemäß der Unsplash-Lizenz