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 beobachtete Interaktion, wobei manchmal Ausreißer ignoriert werden.

Für eine gute Nutzerfreundlichkeit sollten Websites einen „Interaction to Next Paint“-Wert von 200 Millisekunden oder weniger anstreben. Damit Sie dieses Ziel für die meisten Ihrer Nutzer erreichen, ist der 75. Perzentilwert der Seitenladezeiten ein guter Messwert. Dieser sollte für Mobilgeräte und Computer getrennt gemessen werden.

Gute INP-Werte liegen bei 200 Millisekunden oder weniger, schlechte Werte über 500 Millisekunden. Werte dazwischen müssen optimiert werden.

Je nach Website gibt es möglicherweise nur wenige oder gar keine Interaktionen, z. B. Seiten mit hauptsächlich Text und Bildern, die nur wenige oder gar keine interaktiven Elemente enthalten. Bei Websites wie Texteditoren oder Spielen kann es Hunderte oder sogar Tausende von Interaktionen geben. In beiden Fällen, bei denen der INP hoch ist, ist die Nutzerfreundlichkeit gefährdet.

Die Verbesserung der Nutzerfreundlichkeit erfordert Zeit und Mühe, aber das Ergebnis ist eine bessere Nutzererfahrung. In diesem Leitfaden wird ein Weg zur Verbesserung der INP beschrieben.

Herausfinden, was zu einer schlechten INP führt

Bevor Sie langsame Interaktionen beheben können, benötigen Sie Daten, anhand derer Sie erkennen, ob die INP 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 Ihr Weg bei der INP-Optimierung mit Felddaten. Idealerweise liefern Felddaten von einem RUM-Anbieter (Real User Monitoring) nicht nur den INP-Wert einer Seite, sondern auch Kontextdaten, die Aufschluss darüber geben, welche Interaktion für den INP-Wert selbst verantwortlich war, ob die Interaktion während oder nach dem Seitenaufbau stattgefunden hat, die Art der Interaktion (Klick, Tastenanschlag oder Tippen) und andere wertvolle Informationen.

Wenn Sie sich nicht auf einen RUM-Anbieter verlassen, um Felddaten abzurufen, empfehlen wir Ihnen den Leitfaden für die INP-Daten, den Chrome User Experience Report (CrUX) über PageSpeed Insights zu verwenden, um die Lücken zu schließen. CrUX ist der offizielle Datensatz des Core Web Vitals-Programms und bietet eine allgemeine Zusammenfassung von Messwerten für Millionen von Websites, einschließlich INP. CrUX bietet jedoch häufig nicht die Kontextdaten, die Sie von einem RUM-Anbieter erhalten würden, um Probleme zu analysieren. Aus diesem Grund empfehlen wir weiterhin, nach Möglichkeit einen RUM-Anbieter zu verwenden oder eine eigene RUM-Lösung zu implementieren, um die in CrUX verfügbaren Daten zu ergänzen.

Langsame Interaktionen im Lab 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 und sie manuell im Lab 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 beginnt, und endet, wenn die Ereignis-Callbacks für die Interaktion ausgeführt werden.
  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 Gesamtlatenz der Interaktion. Jede Phase einer Interaktion trägt zu einer gewissen Zeit zur Gesamtlatenz der Interaktion bei. Daher ist es wichtig zu wissen, wie Sie jeden Teil der Interaktion so optimieren können, dass er so kurz wie möglich ausgeführt wird.

Eingabeverzögerung ermitteln 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 die Eingabeverzögerungen erheblich 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 Ereignis-Callbacks ausführen 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. Beim Laden einer Seite wird sie zuerst gerendert. Beachten Sie jedoch, dass das Rendern nicht bedeutet, dass die Seite vollständig geladen ist. Je nachdem, wie viele Ressourcen eine Seite benötigt, um vollständig funktionsfähig zu sein, versuchen Nutzer möglicherweise, mit der Seite zu interagieren, während sie noch geladen wird.

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

Je nach Größe eines Scripts können dadurch lange Aufgaben im Haupt-Thread entstehen, was die Reaktion des Browsers auf andere Nutzerinteraktionen verzögert. Damit Ihre Seite während des Seitenaufbaus auf Nutzereingaben reagieren kann, ist es wichtig zu wissen, wie Sie die Wahrscheinlichkeit langer Aufgaben während des Seitenaufbaus verringern können, damit die Seite reaktionsschnell bleibt.

Ereignis-Callbacks optimieren

Die Eingabeverzögerung ist nur der erste Teil dessen, was mit INP gemessen wird. 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.

Oft dem Hauptfaden weichen

Der beste allgemeine Rat zur Optimierung von Ereignis-Callbacks ist, in ihnen so wenig Arbeit wie möglich zu erledigen. Ihre Interaktionslogik kann jedoch komplex sein und Sie können die Arbeit der Nutzer nur geringfügig reduzieren.

Wenn das bei Ihrer Website der Fall ist, können Sie als Nächstes versuchen, die Arbeit in 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 übergebene Rückruf 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.

Undifferenziertes Übergeben ist besser als gar kein Übergeben. Es gibt jedoch eine differenziertere Möglichkeit, den Hauptthread zu übergeben. Dabei wird nur unmittelbar nach einem Ereignis-Callback übergeben, der die Benutzeroberfläche aktualisiert, damit die Renderinglogik früher ausgeführt werden kann.

Yield aktivieren, damit das Rendern früher beginnt

Eine fortgeschrittenere Methode zum Ausgeben besteht darin, den Code in Ihren Ereignis-Callbacks so zu strukturieren, dass nur die Logik ausgeführt wird, die für die Anwendung visueller Updates für den nächsten Frame erforderlich ist. Alles andere kann auf eine nachfolgende Aufgabe verschoben werden. Dadurch bleiben Callbacks nicht nur effizient und wendig, sondern die Renderingzeit für Interaktionen wird auch verbessert, da visuelle Aktualisierungen nicht durch Ereignis-Callback-Code blockiert werden.

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 möglicherweise das Geschriebene speichern, damit Sie bei einem Wechsel zu einer anderen Seite und der Rückkehr nicht Ihre Arbeit verlieren.

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

  1. Aktualisieren Sie das Textfeld mit dem Text, den der Nutzer eingegeben hat, und wenden Sie die erforderliche Formatierung an.
  2. Aktualisieren Sie den Teil der Benutzeroberfläche, in dem die aktuelle Wortanzahl angezeigt wird.
  3. Logik ausführen, um auf Rechtschreibfehler zu prüfen
  4. Speichern Sie die letzten Änderungen (entweder lokal oder in einer Remote-Datenbank).

Der Code dazu 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 sich die Verarbeitungsdauer und damit die Gesamtlatenz der Interaktion durch das Verschieben nicht kritischer Updates auf den nächsten Frame verkürzen lässt.

Eine Darstellung einer Tastaturinteraktion und nachfolgender Aufgaben in zwei Szenarien. In der oberen Abbildung werden die renderkritische Aufgabe und alle nachfolgenden Hintergrundaufgaben synchron ausgeführt, bis die Möglichkeit besteht, einen Frame zu präsentieren. In der unteren Abbildung wird die renderkritische Arbeit zuerst ausgeführt und dann dem Hauptthread übergeben, um einen neuen Frame schneller zu präsentieren. Danach werden die Hintergrundaufgaben ausgeführt.
Klicke auf die Abbildung oben, um eine hochauflösende Version zu sehen.

Die Verwendung von setTimeout() in einem requestAnimationFrame()-Aufruf im vorherigen Codebeispiel ist zwar etwas esoterisch, aber eine effektive Methode, die in allen Browsern funktioniert, um sicherzustellen, dass der nicht kritische Code den nächsten Frame nicht blockiert.

Layout-Flattern vermeiden

Layout-Trashing, manchmal auch als erzwungenes synchrones Layout bezeichnet, ist ein Problem mit der Renderingleistung, bei dem das Layout synchron erfolgt. Das passiert, wenn Sie Stile in JavaScript aktualisieren und dann in derselben Aufgabe lesen. Es gibt viele Eigenschaften in JavaScript, die zu Layout-Überlastungen führen können.

Eine Visualisierung von Layout-Thrashing im Bereich „Leistung“ der Chrome-Entwicklertools
Beispiel für Layout-Trashing, wie im Bereich „Leistung“ der Chrome DevTools zu sehen. Rendering-Aufgaben, die ein Layout-Trashing beinhalten, werden im entsprechenden Teil des Aufrufstacks mit einem roten Dreieck in der oberen rechten Ecke gekennzeichnet. Sie sind oft mit Stil neu berechnen oder Layout gekennzeichnet.

Layout-Trashing ist ein Leistungsengpass, da der Browser durch das Aktualisieren von Stilen und das sofortige Anfordern der Werte dieser Stile in JavaScript gezwungen ist, synchrone Layoutarbeiten auszuführen, die er sonst asynchron ausführen könnte, nachdem die Ereignis-Callbacks beendet sind.

Verzögerung bei der Präsentation minimieren

Die Darstellungsverzögerung einer Interaktion beginnt, wenn die Ereignis-Callbacks einer Interaktion beendet sind, und endet, wenn der Browser den nächsten Frame mit den sich daraus ergebenden visuellen Änderungen zeichnen kann.

DOM-Größe minimieren

Wenn das DOM einer Seite klein ist, ist das Rendering in der Regel schnell abgeschlossen. Wenn DOMs jedoch sehr groß werden, steigt der Rendering-Aufwand mit zunehmender DOM-Größe. Das Verhältnis zwischen dem Aufwand für das Rendern und der Größe des DOM ist nicht linear, aber das Rendern großer DOMs erfordert mehr Aufwand als das Rendern kleiner DOMs. Ein großes DOM ist in zwei Fällen problematisch:

  1. Beim ersten Rendern der Seite, wenn ein großes DOM viel Arbeit erfordert, um den ursprünglichen Zustand der Seite zu rendern.
  2. Als Reaktion auf eine Nutzerinteraktion, bei der ein großes DOM zu sehr aufwendigen Rendering-Aktualisierungen führen kann und daher die Zeit verlängert, die der Browser für die 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. durch Ebenenreduktion oder das Hinzufügen von Elementen zum DOM während Nutzerinteraktionen, um die anfängliche DOM-Größe klein zu halten, aber diese Techniken sind möglicherweise nicht ausreichend.

content-visibility zum Lazy-Rendering von Elementen außerhalb des sichtbaren Bereichs verwenden

Mit der CSS-Eigenschaft content-visibility können Sie sowohl die Menge an Rendering-Arbeit beim Seitenaufbau als auch die Menge an Rendering-Arbeit in Reaktion auf Nutzerinteraktionen begrenzen. Das entspricht dem Lazy-Rendering von Elementen, wenn sie sich dem Darstellungsbereich nähern. Es kann etwas dauern, bis Sie content-visibility effektiv einsetzen können. Es lohnt sich aber, zu prüfen, ob sich dadurch die Renderingzeit verkürzt und die INP Ihrer Seite verbessert.

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, wird es im Browser als Stream empfangen. Beim Streaming wird die HTML-Antwort vom Server in mehreren Teilen empfangen. 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.

Beim ersten Besuch einer Website ist immer ein wenig HTML erforderlich. Ein gängiger Ansatz besteht darin, mit einem minimalen Anfangs-HTML-Code zu beginnen und dann JavaScript zu verwenden, um den Inhaltsbereich zu füllen. Auch spätere Aktualisierungen dieses Inhaltsbereichs erfolgen aufgrund von Nutzerinteraktionen. Dies wird in der Regel als SPA-Modell (Single-Page-Anwendung) bezeichnet. Ein Nachteil dieses Musters ist, dass beim Rendern von HTML mit JavaScript auf dem Client nicht nur die Kosten für die JavaScript-Verarbeitung zum Erstellen dieser HTML-Datei anfallen, sondern auch, dass der Browser nicht reagiert, bis das Parsen und Rendern der HTML-Datei abgeschlossen ist.

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. Das ist in der Regel in Ordnung, solange Sie nicht große Mengen an HTML-Code 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 im Feld beheben, werden Sie wahrscheinlich – insbesondere, wenn Ihre Website viele interaktive Elemente enthält – weitere langsame Interaktionen finden, die Sie ebenfalls optimieren müssen.

Beharrlichkeit ist der Schlüssel zur Verbesserung von INP. Mit der Zeit können Sie die Responsivität Ihrer Seite so anpassen, dass Nutzer mit der von Ihnen bereitgestellten Umgebung zufrieden sind. Es ist auch wahrscheinlich, dass Sie bei der Entwicklung neuer Funktionen für Ihre Nutzer möglicherweise denselben Prozess durchlaufen müssen, um Interaktionen speziell für diese Nutzer zu optimieren. Das erfordert Zeit und Mühe, aber es lohnt sich.

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