Interaktion für den nächsten Farbauftrag optimieren

Hier erfahren Sie, wie Sie „Interaction to Next Paint“ für Ihre Website optimieren können.

Interaction to Next Paint (INP) ist ein stabiler Core Web Vitals-Messwert, mit dem bewertet wird, wie gut eine Seite insgesamt auf Nutzerinteraktionen reagiert. Dazu wird die Latenz aller qualifizierenden Interaktionen erfasst, die während des Besuchs eines Nutzers auf einer Seite auftreten. Der endgültige INP-Wert ist die längste Interaktion, die beobachtet wurde (manchmal werden Ausreißer ignoriert).

Für eine gute Nutzerfreundlichkeit sollten Websites eine Interaction to Next Paint von 200 Millisekunden oder weniger anstreben. Damit Sie dieses Ziel für die meisten Ihrer Nutzer erreichen, empfiehlt es sich, das 75. Perzentil der Seitenladevorgänge zu messen, das nach Mobil- und Desktopgeräten segmentiert ist.

Gute INP-Werte liegen bei maximal 200 Millisekunden, schlechte Werte über 500 Millisekunden und alles dazwischen muss verbessert werden.

Je nach Website sind nur wenige oder gar keine Interaktionen erforderlich, z. B. Seiten, die hauptsächlich Text und Bilder enthalten und nur wenige oder gar keine interaktiven Elemente enthalten. Bei Websites wie Texteditoren oder Spielen könnten es Hunderte oder sogar Tausende von Interaktionen geben. In jedem Fall mit einem hohen INP-Wert ist die Nutzererfahrung gefährdet.

Es erfordert Zeit und Mühe, INP zu verbessern, aber durch die Prämie wird die Nutzererfahrung verbessert. In diesem Leitfaden wird ein Weg zur Verbesserung von INP erläutert.

Die Ursache eines schlechten INP ermitteln

Bevor Sie langsame Interaktionen korrigieren können, benötigen Sie Daten, aus denen hervorgeht, ob der INP-Wert Ihrer Website schlecht ist oder verbessert werden muss. Sobald Sie diese Informationen haben, können Sie mit der Diagnose langsamer Interaktionen beginnen und sich auf eine Lösung vorarbeiten.

Langsame Interaktionen vor Ort finden

Im Idealfall beginnt Ihre Vorgehensweise bei der Optimierung von INP mit Felddaten. Im Idealfall liefern Felddaten eines RUM-Anbieters (Real User Monitoring) nicht nur den INP-Wert einer Seite, sondern auch Kontextdaten, die Aufschluss darüber geben, welche spezifische Interaktion für den INP-Wert selbst verantwortlich war, ob die Interaktion während oder nach dem Seitenaufbau erfolgte, die Art der Interaktion (Klicken, Drücken oder Tippen) und andere wertvolle Informationen.

Wenn Sie zum Abrufen von Felddaten nicht auf einen RUM-Anbieter angewiesen sind, wird im Leitfaden für die INP-Daten empfohlen, den Chrome User Experience Report (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 allgemeine Zusammenfassung der Messwerte für Millionen von Websites, einschließlich INP. CrUX stellt jedoch häufig nicht die kontextbezogenen Daten bereit, die Sie von einem RUM-Anbieter erhalten würden, um Ihnen bei der Analyse von Problemen zu helfen. Aus diesem Grund empfehlen wir Websites, nach Möglichkeit einen RUM-Anbieter zu verwenden oder eine eigene RUM-Lösung zu implementieren, um die in CrUX verfügbaren Funktionen zu ergänzen.

Langsame Interaktionen im Labor diagnostizieren

Idealerweise sollten Sie mit den Tests im Labor beginnen, sobald Sie Felddaten haben, die auf langsame Interaktionen hindeuten. Wenn keine Felddaten vorhanden sind, gibt es einige Strategien, um langsame Interaktionen im Labor zu identifizieren. Zu diesen Strategien gehören das Verfolgen gängiger User Flows und das Testen von Interaktionen auf dem Weg sowie die Interaktion mit der Seite während des Ladevorgangs – wenn der Hauptthread häufig aktiv ist –, um langsame Interaktionen in diesem entscheidenden Teil der User Experience anzuzeigen.

Interaktionen optimieren

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

  1. Die Eingabeverzögerung, die beginnt, wenn der Nutzer eine Interaktion mit der Seite initiiert, und endet, wenn der Ereignis-Callback für die Interaktion beginnt.
  2. Die Verarbeitungsdauer, die sich aus der Zeit ergibt, die für die Ausführung von Ereignis-Callbacks benötigt wird.
  3. Die Darstellungsverzögerung, also die Zeit, die der Browser benötigt, um den nächsten Frame zu präsentieren, der das visuelle Ergebnis der Interaktion enthält.

Die Summe dieser drei Phasen ist die gesamte Interaktionslatenz. Jede einzelne Phase einer Interaktion trägt in gewisser Weise zur Gesamtlatenz der Interaktion bei. Daher ist es wichtig zu wissen, wie Sie jeden Teil der Interaktion optimieren können, damit die Interaktion so wenig Zeit wie möglich dauert.

Eingabeverzögerung identifizieren und reduzieren

Wenn ein Nutzer mit einer Seite interagiert, ist der erste Teil dieser Interaktion die Eingabeverzögerung. Je nach anderen Aktivitäten auf der Seite können Eingabeverzögerungen beträchtlich sein. Dies könnte auf eine Aktivität im Hauptthread zurückzuführen sein (z. B. auf das Laden, Parsen und Kompilieren von Skripts), auf Abrufbehandlung, Timer-Funktionen oder sogar auf andere Interaktionen, die schnell hintereinander erfolgen und sich überschneiden.

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

Beziehung zwischen Skriptbewertung und langen Aufgaben während des Starts

Ein wichtiger Aspekt der Interaktivität im Lebenszyklus einer Seite ist der Startvorgang. Wenn eine Seite geladen wird, wird sie anfangs gerendert. Allerdings bedeutet das Rendering einer Seite nicht automatisch, dass sie vollständig geladen wurde. Je nachdem, wie viele Ressourcen eine Seite benötigt, um voll funktionsfähig zu sein, ist es möglich, dass Nutzer versuchen, mit der Seite zu interagieren, während sie noch geladen wird.

Eine Sache, die die Eingabeverzögerung einer Interaktion beim Laden einer Seite verlängern kann, ist die Skriptbewertung. Nachdem eine JavaScript-Datei aus dem Netzwerk abgerufen wurde, hat der Browser noch einige Schritte zu erledigen, 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 das anschließende Ausführen.

Je nach Größe eines Skripts kann dies zu langen Aufgaben im Hauptthread führen, wodurch der Browser verzögert auf andere Nutzerinteraktionen reagiert. Damit Ihre Seite beim Seitenaufbau responsiv auf Nutzereingaben reagiert, sollten Sie wissen, was Sie tun können, um die Wahrscheinlichkeit langer Aufgaben während des Seitenaufbaus zu verringern, damit die Seite flüssig bleibt.

Ereignisrückrufe optimieren

Die Eingabeverzögerung ist nur der erste Teil der von INP gemessenen Maßnahmen. Außerdem müssen Sie dafür sorgen, dass die Ereignis-Callbacks, die als Reaktion auf eine Nutzerinteraktion ausgeführt werden, so schnell wie möglich abgeschlossen werden können.

Dem Hauptthread häufig nachgeben

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

Wenn Sie der Meinung sind, dass dies für Ihre Website der Fall ist, können Sie als Nächstes versuchen, die Arbeit bei Ereignis-Callbacks in separate Aufgaben aufzuteilen. Dadurch wird verhindert, dass die kollektive Arbeit zu einer langen Aufgabe wird, die den Hauptthread blockiert. Dadurch werden andere Interaktionen, die andernfalls auf den Hauptthread warten würden, früher ausgeführt.

setTimeout ist eine Möglichkeit, Aufgaben aufzuteilen, da der an sie übergebene Callback in einer neuen Aufgabe ausgeführt wird. Du kannst setTimeout alleine verwenden oder die Verwendung in einer separaten Funktion abstrahieren, um die Erträge zu steigern.

Unbestimmtes Ergeben ist besser, als überhaupt keinen Ertrag zu erzielen. Es gibt jedoch eine differenziertere Möglichkeit, dem Hauptthread nachzugeben, und dabei wird erst unmittelbar nach einem Ereignis-Callback geliefert, der die Benutzeroberfläche aktualisiert, sodass die Rendering-Logik früher ausgeführt werden kann.

Ausgeben, damit Rendering-Arbeiten früher ausgeführt werden können

Bei einer fortgeschritteneren Methode wird der Code in Ihren Ereignis-Callbacks strukturiert, um die Ausführung auf die Logik zu beschränken, die zum Anwenden visueller Aktualisierungen für den nächsten Frame erforderlich ist. Alles andere kann auf eine nachfolgende Aufgabe übertragen werden. So bleiben Callbacks nicht nur leicht und flexibel, sondern es wird auch die Renderingzeit für Interaktionen verbessert, da visuelle Aktualisierungen nicht auf den Ereignis-Callback-Code blockiert werden können.

Stellen Sie sich beispielsweise einen Rich-Text-Editor vor, der den Text während der Eingabe formatiert, aber auch andere Aspekte der Benutzeroberfläche entsprechend Ihrem Geschriebenen aktualisiert (z. B. die Anzahl der Wörter, das Hervorheben von Rechtschreibfehlern und anderes wichtiges visuelles Feedback). Außerdem muss die Anwendung eventuell Ihren Text speichern, damit Sie beim Verlassen und Zurückkehren keine Arbeit verlieren.

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

  1. Geben Sie im Textfeld die eingegebenen Informationen ein 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 eine Logik zur Prüfung auf Rechtschreibfehler aus.
  4. Speichern Sie die letzten Änderungen (entweder lokal oder in einer Remote-Datenbank).

Der entsprechende Code könnte in 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 Verschieben nicht kritischer Aktualisierungen bis nach dem nächsten Frame die Verarbeitungsdauer und damit die Gesamt-Interaktionslatenz reduzieren kann.

Darstellung einer Interaktion mit der Tastatur und der nachfolgenden 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 Präsentation eines Frames gekommen ist. In der unteren Abbildung wird die Rendering-Wichtige Arbeit zuerst ausgeführt und geht dann dem Hauptthread über, um früher einen neuen Frame zu präsentieren. Die Hintergrundaufgaben werden danach ausgeführt.
Klicke auf die Abbildung oben, um eine hochauflösende Version zu sehen.

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

Layout-Thrashing vermeiden

Layout-Thrashing, manchmal auch als erzwungenes synchrones Layout bezeichnet, ist ein Leistungsproblem bei der Rendering-Leistung, bei dem das Layout synchron erfolgt. Er tritt auf, wenn Sie Stile in JavaScript aktualisieren und dann in derselben Aufgabe lesen. Es gibt viele Eigenschaften in JavaScript, die zu Layout-Flachen führen können.

Eine Visualisierung von Layout-Thrashing im Bereich „Leistung“ der Chrome-Entwicklertools
Ein Beispiel für Layout-Thrashing, das im Bereich „Leistung“ der Chrome-Entwicklertools zu sehen ist. Renderingaufgaben, die Layout-Thrashing beinhalten, sind mit einem roten Dreieck in der oberen rechten Ecke des Teils des Aufrufstacks gekennzeichnet, der häufig mit Stil neu berechnen oder Layout beschriftet ist.

Layout-Drashing stellt einen Leistungsengpass dar, da der Browser durch das Aktualisieren von Stilen und anschließende sofortige Anforderung der Werte dieser Stile in JavaScript gezwungen wäre, synchrone Layoutarbeiten auszuführen, die sonst später asynchron hätten ausgeführt werden können, nachdem die Ereignis-Callbacks ausgeführt wurden.

Verzögerung bei der Präsentation minimieren

Die Darstellungsverzögerung von Interaktionsmarkierungen erstreckt sich von der Ausführung der Ereignis-Callbacks einer Interaktion bis zu dem Punkt, an dem der Browser den nächsten Frame mit den resultierenden visuellen Änderungen darstellen kann.

DOM-Größe minimieren

Wenn das DOM einer Seite klein ist, wird das Rendering in der Regel schnell abgeschlossen. Wenn DOMs jedoch sehr groß werden, nimmt die Rendering-Arbeit tendenziell mit zunehmender DOM-Größe zu. Das Verhältnis 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. Beim ersten Rendern der Seite, bei dem ein großes DOM viel Arbeit für das Rendern des Ausgangszustands der Seite erfordert.
  2. Als Reaktion auf eine Nutzerinteraktion, bei der ein großes DOM die Rendering-Aktualisierungen sehr kostspielig macht und daher die Zeit erhöht, die der Browser zur Darstellung des nächsten Frames benötigt.

Denken Sie daran, dass es Fälle gibt, in denen große DOMs nicht wesentlich reduziert werden können. Es gibt zwar Möglichkeiten, die DOM-Größe zu reduzieren, z. B. das Vereinfachen Ihres DOMs oder das Hinzufügen zum DOM bei Nutzerinteraktionen, um die anfängliche DOM-Größe klein zu halten. Diese Techniken greifen möglicherweise nur bis zu einem bestimmten Punkt zu.

Mit content-visibility kannst du Elemente außerhalb des Bildschirms verzögert rendern

Eine Möglichkeit, sowohl den Rendering-Aufwand beim Seitenaufbau als auch den Rendering-Aufwand als Reaktion auf Nutzerinteraktionen zu begrenzen, besteht darin, die CSS-Eigenschaft content-visibility zu verwenden. Dies hat den Vorteil, dass Elemente verzögert gerendert werden, wenn sie sich dem Darstellungsbereich nähern. Die effektive Nutzung von content-visibility kann zwar einige Übung erfordern, es lohnt sich aber, zu prüfen, ob dadurch die Renderingzeit kürzer ist und sich dadurch der INP-Wert deiner Seite verbessern lässt.

Beachten Sie die Leistungskosten beim Rendern von HTML mit JavaScript.

Wenn HTML vorhanden ist, wird HTML geparst und nachdem der Browser das Parsen von HTML in ein DOM abgeschlossen hat, muss er Stile darauf anwenden, Layoutberechnungen durchführen und anschließend das Layout rendern. Diese Kosten sind unvermeidbar, aber es ist wichtig, wie Sie beim Rendern von HTML vorgehen.

Wenn der Server HTML sendet, kommt dieser im Browser als Stream an. Streaming bedeutet, dass die HTML-Antwort vom Server in Blöcken eingeht. Der Browser optimiert die Verarbeitung eines Streams, indem er Blöcke des Streams beim Eintreffen inkrementell parst und diese Stück für Stück wiedergibt. Diese Leistungsoptimierung erfolgt insofern, als der Browser beim Seitenaufbau implizit regelmäßig und automatisch Erträge ausgibt, die Sie kostenlos erhalten.

Während der erste Besuch auf einer Website immer etwas an HTML erfordert, beginnt ein gängiger Ansatz mit einem minimalen HTML-Anfang. Anschließend wird JavaScript verwendet, um den Inhaltsbereich auszufüllen. Dieser Inhaltsbereich wird auch später durch Nutzerinteraktionen aktualisiert. Dies wird normalerweise als SPA-Modell (Single-Page Application Model) bezeichnet. Ein Nachteil dieses Musters besteht darin, dass durch das Rendern von HTML mit JavaScript auf dem Client nicht nur die Kosten für die JavaScript-Verarbeitung zur Erstellung dieses HTML entstehen, sondern auch der Browser erst nach dem Parsen und Rendern des HTML-Codes.

Denken Sie jedoch daran, dass auch bei Websites, die keine SPAs sind, ein gewisses Maß an HTML-Rendering durch JavaScript als Ergebnis von Interaktionen erforderlich ist. Dies ist in der Regel in Ordnung, solange Sie keine großen Mengen an HTML auf dem Client rendern, was die Darstellung des nächsten Frames verzögern kann. Es ist jedoch wichtig, zu verstehen, welche Auswirkungen dieser Ansatz auf die Leistung beim Rendern von HTML im Browser hat und wie er sich auf die Reaktionsschnelligkeit Ihrer Website auf Nutzereingaben auswirken kann, wenn Sie viel HTML-Code über JavaScript rendern.

Fazit

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

Beharrlichkeit ist der Schlüssel zur Verbesserung von INP. Mit der Zeit können Sie dafür sorgen, dass Ihre Seite so reaktionsfreudig wie möglich wird. Die Chancen stehen außerdem gut, dass Sie bei der Entwicklung neuer Funktionen für Ihre Nutzer denselben Prozess durchlaufen müssen, um spezifische Interaktionen zu optimieren. 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