Informationen zu Interaction to Next Paint (INP)

1. Einführung

Eine interaktive Demo und ein Codelab zum Messwert Interaction to Next Paint (INP).

Ein Diagramm, das eine Interaktion im Hauptthread darstellt. Der Nutzer gibt eine Eingabe ein, während Aufgaben blockiert werden. Die Eingabe wird verzögert, bis diese Aufgaben abgeschlossen sind. Danach werden die Event-Listener für „pointerup“, „mouseup“ und „click“ ausgeführt. Anschließend werden Rendering- und Zeichenvorgänge gestartet, bis der nächste Frame präsentiert wird.

Vorbereitung

Lerninhalte

  • Wie sich das Zusammenspiel von Nutzerinteraktionen und der Art und Weise, wie Sie diese Interaktionen verarbeiten, auf die Reaktionsfähigkeit der Seite auswirkt.
  • So lassen sich Verzögerungen reduzieren und vermeiden, um eine reibungslose Nutzererfahrung zu ermöglichen.

Voraussetzungen

  • Ein Computer, auf dem Code von GitHub geklont und npm-Befehle ausgeführt werden können.
  • Einen Texteditor.
  • Eine aktuelle Version von Chrome, damit alle Interaktionsmessungen funktionieren.

2. Einrichten

Code abrufen und ausführen

Der Code befindet sich im Repository web-vitals-codelabs.

  1. Klonen Sie das Repository in Ihrem Terminal: git clone https://github.com/GoogleChromeLabs/web-vitals-codelabs.git
  2. Wechseln Sie in das geklonte Verzeichnis: cd web-vitals-codelabs/understanding-inp
  3. Abhängigkeiten installieren: npm ci
  4. Starten Sie den Webserver: npm run start
  5. Rufen Sie in Ihrem Browser http://localhost:5173/understanding-inp/ auf.

Übersicht über die App

Oben auf der Seite befindet sich ein Zähler für die Punktzahl und eine Schaltfläche Erhöhen. Ein klassisches Beispiel für Reaktivität und Reaktionsfähigkeit.

Screenshot der Demo-App für dieses Codelab

Unter der Schaltfläche sehen Sie vier Messwerte:

  • INP: Der aktuelle INP-Wert, der in der Regel die schlechteste Interaktion darstellt.
  • Interaktion: Die Punktzahl der letzten Interaktion.
  • FPS: Die Frames pro Sekunde des Hauptthreads der Seite.
  • Timer: Eine laufende Timer-Animation zur Visualisierung von Rucklern.

Die Einträge für FPS und Timer sind für die Messung von Interaktionen nicht erforderlich. Sie werden nur hinzugefügt, um die Visualisierung der Reaktionsfähigkeit zu erleichtern.

Jetzt ausprobieren

Klicken Sie auf die Schaltfläche Increment (Erhöhen) und sehen Sie zu, wie sich der Wert erhöht. Ändern sich die Werte für INP und Interaktion mit jedem Inkrement?

INP misst, wie lange es dauert, bis die Seite dem Nutzer die gerenderte Aktualisierung anzeigt, nachdem er mit ihr interagiert hat.

3. Interaktionen mit den Chrome-Entwicklertools messen

Öffnen Sie die Entwicklertools über das Menü Weitere Tools > Entwicklertools, indem Sie mit der rechten Maustaste auf die Seite klicken und Untersuchen auswählen oder indem Sie einen Tastenkürzel verwenden.

Wechseln Sie zum Bereich Leistung, in dem Sie Interaktionen messen können.

Screenshot des Bereichs „Leistung“ in den Entwicklertools neben der App

Erfassen Sie als Nächstes eine Interaktion im Bereich „Leistung“.

  1. Tippen Sie auf die Schaltfläche zum Aufzeichnen.
  2. Interagieren Sie mit der Seite (drücken Sie die Schaltfläche Increment).
  3. Beenden Sie die Aufnahme.

Auf der resultierenden Zeitachse finden Sie einen Track mit Interaktionen. Klicken Sie links auf das Dreieck, um den Bereich zu maximieren.

Eine animierte Demonstration der Aufzeichnung einer Interaktion mit dem Leistungsbereich der DevTools

Es werden zwei Interaktionen angezeigt. Zoomen Sie auf das zweite Bild heran, indem Sie scrollen oder die W-Taste gedrückt halten.

Screenshot des Leistungsbereichs der Entwicklertools. Der Cursor bewegt sich über die Interaktion im Bereich und ein Kurzinfo-Fenster mit dem kurzen Timing der Interaktion wird angezeigt.

Wenn Sie den Mauszeiger auf die Interaktion bewegen, sehen Sie, dass sie schnell war, keine Zeit für die Verarbeitungsdauer benötigt hat und nur eine minimale Zeit für die Eingabeverzögerung und Darstellungsverzögerung. Die genauen Zeitspannen hängen von der Geschwindigkeit Ihres Computers ab.

4. Lang andauernde Event-Listener

Öffnen Sie die Datei index.js und entfernen Sie die Kommentarzeichen vor der Funktion blockFor im Event-Listener.

Vollständiger Code: click_block.html

button.addEventListener('click', () => {
  blockFor(1000);
  score.incrementAndUpdateUI();
});

Speichern Sie die Datei. Der Server erkennt die Änderung und aktualisiert die Seite für Sie.

Versuchen Sie noch einmal, mit der Seite zu interagieren. Die Interaktionen werden jetzt deutlich langsamer ausgeführt.

Leistungsanalyse

Nehmen Sie eine weitere Aufzeichnung im Bereich „Leistung“ vor, um zu sehen, wie das dort aussieht.

Eine Sekunde lange Interaktion im Bereich „Leistung“

Was früher eine kurze Interaktion war, dauert jetzt eine volle Sekunde.

Wenn Sie den Mauszeiger auf die Interaktion bewegen, sehen Sie, dass die Zeit fast vollständig für die „Verarbeitungsdauer“ aufgewendet wird. Das ist die Zeit, die für die Ausführung der Event-Listener-Callbacks benötigt wird. Da der blockierende blockFor-Aufruf vollständig innerhalb des Event-Listeners erfolgt, wird die Zeit dort verbracht.

5. Test: Verarbeitungsdauer

Probieren Sie verschiedene Möglichkeiten aus, die Arbeit des Event-Listeners neu anzuordnen, um die Auswirkungen auf INP zu sehen.

Benutzeroberfläche zuerst aktualisieren

Was passiert, wenn Sie die Reihenfolge der JS-Aufrufe ändern – zuerst die Benutzeroberfläche aktualisieren und dann blockieren?

Vollständiger Code: ui_first.html

button.addEventListener('click', () => {
  score.incrementAndUpdateUI();
  blockFor(1000);
});

Ist Ihnen aufgefallen, dass die Benutzeroberfläche früher angezeigt wurde? Wirkt sich die Reihenfolge auf INP-Werte aus?

Erstellen Sie einen Trace und untersuchen Sie die Interaktion, um festzustellen, ob es Unterschiede gab.

Listener trennen

Was passiert, wenn Sie die Arbeit in einen separaten Event-Listener verschieben? Aktualisieren Sie die Benutzeroberfläche in einem Event-Listener und blockieren Sie die Seite in einem separaten Listener.

Vollständiger Code: two_click.html

button.addEventListener('click', () => {
  score.incrementAndUpdateUI();
});

button.addEventListener('click', () => {
  blockFor(1000);
});

Wie sieht das jetzt im Bereich „Leistung“ aus?

Verschiedene Ereignistypen

Bei den meisten Interaktionen werden viele Arten von Ereignissen ausgelöst, von Zeiger- oder Tastaturereignissen bis hin zu Hover-, Fokus-/Unfokus- und synthetischen Ereignissen wie „beforechange“ und „beforeinput“.

Viele echte Seiten haben Listener für viele verschiedene Ereignisse.

Was passiert, wenn Sie die Ereignistypen für die Ereignis-Listener ändern? Ersetzen Sie beispielsweise einen der click-Event-Listener durch pointerup oder mouseup.

Vollständiger Code: diff_handlers.html

button.addEventListener('click', () => {
  score.incrementAndUpdateUI();
});

button.addEventListener('pointerup', () => {
  blockFor(1000);
});

Kein Update der Benutzeroberfläche

Was passiert, wenn Sie den Aufruf zum Aktualisieren der Benutzeroberfläche aus dem Ereignis-Listener entfernen?

Vollständiger Code: no_ui.html

button.addEventListener('click', () => {
  blockFor(1000);
  // score.incrementAndUpdateUI();
});

6. Verarbeitungsdauer von Testergebnissen

Leistungs-Trace: Benutzeroberfläche zuerst aktualisieren

Vollständiger Code: ui_first.html

button.addEventListener('click', () => {
  score.incrementAndUpdateUI();
  blockFor(1000);
});

Wenn Sie sich eine Aufzeichnung des Leistungsbereichs ansehen, in der auf die Schaltfläche geklickt wird, sehen Sie, dass sich die Ergebnisse nicht geändert haben. Obwohl eine Aktualisierung der Benutzeroberfläche vor dem blockierenden Code ausgelöst wurde, hat der Browser die Darstellung auf dem Bildschirm erst nach Abschluss des Event-Listeners aktualisiert. Die Interaktion hat also immer noch etwas mehr als eine Sekunde gedauert.

Eine Interaktion von einer Sekunde im Bereich „Leistung“

Leistungs-Trace: Separate Listener

Vollständiger Code: two_click.html

button.addEventListener('click', () => {
  score.incrementAndUpdateUI();
});

button.addEventListener('click', () => {
  blockFor(1000);
});

Auch hier gibt es funktional keinen Unterschied. Die Interaktion dauert weiterhin eine volle Sekunde.

Wenn Sie die Klickinteraktion vergrößern, sehen Sie, dass tatsächlich zwei verschiedene Funktionen als Ergebnis des click-Ereignisses aufgerufen werden.

Wie erwartet wird die erste Aufgabe – die Aktualisierung der Benutzeroberfläche – sehr schnell ausgeführt, während die zweite eine volle Sekunde dauert. Die Summe ihrer Auswirkungen führt jedoch zu derselben langsamen Interaktion für den Endnutzer.

Eine vergrößerte Ansicht der einsekündigen Interaktion in diesem Beispiel. Der erste Funktionsaufruf dauert weniger als eine Millisekunde.

Leistungs-Trace: verschiedene Ereignistypen

button.addEventListener('click', () => {
  score.incrementAndUpdateUI();
});

button.addEventListener('pointerup', () => {
  blockFor(1000);
});

Diese Ergebnisse sind sehr ähnlich. Die Interaktion dauert weiterhin eine volle Sekunde. Der einzige Unterschied besteht darin, dass der kürzere click-Listener, der nur die Benutzeroberfläche aktualisiert, jetzt nach dem blockierenden pointerup-Listener ausgeführt wird.

Eine vergrößerte Ansicht der einsekündigen Interaktion in diesem Beispiel, in der zu sehen ist, dass der Click-Event-Listener nach dem Pointerup-Listener weniger als eine Millisekunde benötigt.

Leistungs-Trace: Keine Aktualisierung der Benutzeroberfläche

Vollständiger Code: no_ui.html

button.addEventListener('click', () => {
  blockFor(1000);
  // score.incrementAndUpdateUI();
});
  • Der Wert wird nicht aktualisiert, die Seite aber schon.
  • Animationen, CSS-Effekte, Standardaktionen für Webkomponenten (Formulareingabe), Texteingabe und Texthervorhebung werden weiterhin aktualisiert.

In diesem Fall wechselt die Schaltfläche beim Klicken in den aktiven Status und wieder zurück. Dazu muss der Browser die Seite neu rendern. Es gibt also weiterhin einen INP.

Da der Event-Listener den Hauptthread eine Sekunde lang blockiert hat, wodurch die Seite nicht gerendert werden konnte, dauert die Interaktion immer noch eine volle Sekunde.

Wenn Sie eine Aufzeichnung des Leistungspanels erstellen, wird die Interaktion nahezu identisch mit den vorherigen Interaktionen angezeigt.

Eine Interaktion von einer Sekunde im Bereich „Leistung“

Fazit

Jeder Code, der in einem beliebigen Ereignis-Listener ausgeführt wird, verzögert die Interaktion.

  • Dazu gehören Listener, die über verschiedene Skripts registriert wurden, sowie Framework- oder Bibliothekscode, der in Listenern ausgeführt wird, z. B. eine Statusaktualisierung, die das Rendern einer Komponente auslöst.
  • Nicht nur Ihr eigener Code, sondern auch alle Drittanbieter-Scripts.

Das ist ein häufiges Problem.

Schließlich gilt: Nur weil Ihr Code keinen Paint-Vorgang auslöst, heißt das nicht, dass kein Paint-Vorgang auf langsame Event-Listener wartet.

7. Test: Eingabeverzögerung

Was ist mit Code, der außerhalb von Event-Listenern lange ausgeführt wird? Beispiel:

  • Wenn Sie ein spät ladendes <script> hatten, das die Seite während des Ladevorgangs zufällig blockiert hat.
  • Ein API-Aufruf, z. B. setInterval, der die Seite regelmäßig blockiert?

Entfernen Sie blockFor aus dem Event-Listener und fügen Sie es einem setInterval() hinzu:

Vollständiger Code: input_delay.html

setInterval(() => {
  blockFor(1000);
}, 3000);


button.addEventListener('click', () => {
  score.incrementAndUpdateUI();
});

Was ändert sich?

8. Ergebnisse des Tests zur Eingabeverzögerung

Vollständiger Code: input_delay.html

setInterval(() => {
  blockFor(1000);
}, 3000);


button.addEventListener('click', () => {
  score.incrementAndUpdateUI();
});

Wenn ein Schaltflächenklick aufgezeichnet wird, der zufällig während der Ausführung des blockierenden Tasks setInterval erfolgt, führt dies zu einer Interaktion mit langer Ausführungszeit, auch wenn in der Interaktion selbst keine blockierenden Arbeiten ausgeführt werden.

Diese langen Zeiträume werden oft als „Long Tasks“ bezeichnet.

Wenn Sie in den DevTools mit dem Mauszeiger auf die Interaktion zeigen, sehen Sie, dass die Interaktionszeit jetzt hauptsächlich auf die Eingabeverzögerung und nicht auf die Verarbeitungsdauer zurückzuführen ist.

Das DevTools-Leistungs-Panel zeigt eine blockierende Aufgabe von einer Sekunde, eine Interaktion, die während dieser Aufgabe erfolgt, und eine Interaktion von 642 Millisekunden, die hauptsächlich auf die Eingabelatenz zurückzuführen ist.

Beachten Sie, dass sich dies nicht immer auf die Interaktionen auswirkt. Wenn Sie während der Ausführung der Aufgabe nicht klicken, haben Sie vielleicht Glück. Solche „zufälligen“ Nieser können sehr schwer zu debuggen sein, wenn sie nur manchmal Probleme verursachen.

Eine Möglichkeit, diese Probleme zu finden, besteht darin, lange Aufgaben (oder Long Animation Frames) und die Total Blocking Time zu messen.

9. Langsame Präsentation

Bisher haben wir uns die Leistung von JavaScript über Eingabeverzögerung oder Event-Listener angesehen. Was wirkt sich aber noch auf das Rendern des nächsten Paint aus?

Nun, die Seite mit teuren Effekten zu aktualisieren!

Auch wenn die Seiten schnell aktualisiert werden, muss der Browser möglicherweise viel Arbeit leisten, um sie zu rendern.