Messen · 2 Min. Lesezeit
INP zerlegen: wo die Zeit zwischen Klick und Bild bleibt
Interaction to Next Paint misst, wie schnell eine Seite sichtbar reagiert. Die Zahl besteht aus drei Teilen, und jeder hat eine andere Ursache.
Seit 2024 ist Interaction to Next Paint (INP) eine der Core Web Vitals und hat First Input Delay abgelöst. INP betrachtet alle Klicks, Taps und Tastendrücke während eines Seitenbesuchs und meldet ungefähr die langsamste davon – gemessen vom Moment der Eingabe bis zu dem Bild, in dem die Reaktion zu sehen ist. Scrollen und Hover zählen nicht.
Als gut gelten Werte bis 200 ms, bis 500 ms verbesserungswürdig, darüber schlecht – jeweils bezogen auf das 75. Perzentil aller Besuche.
Drei Teile
- Eingabeverzögerung – die Zeit, bis der erste Event-Handler starten kann. Sie entsteht, wenn der Main Thread gerade mit etwas anderem beschäftigt ist, typischerweise mit einem langen Task.
- Verarbeitung – die Laufzeit aller Handler für dieses Event, also der eigene Code.
- Darstellungsverzögerung – vom Ende der Handler bis zum nächsten Bild:
requestAnimationFrame-Callbacks, Style, Layout, Paint. Große Änderungen am DOM machen diesen Teil teuer.
Messen
Die Event Timing API liefert für jede Interaktion alle drei Teile. Einträge mit derselben interactionId gehören zur selben Interaktion (zum Beispiel pointerdown, pointerup und click):
new PerformanceObserver((list) => {
for (const e of list.getEntries()) {
if (!e.interactionId) continue; // kein Klick, Tap oder Tastendruck
const inputDelay = e.processingStart - e.startTime;
const processing = e.processingEnd - e.processingStart;
const presentation = e.startTime + e.duration - e.processingEnd;
console.log(e.name, e.duration, { inputDelay, processing, presentation });
}
}).observe({ type: 'event', durationThreshold: 16, buffered: true });
durationThreshold legt fest, ab welcher Dauer Einträge gemeldet werden; 16 ms ist das Minimum, ohne Angabe sind es 104 ms. Die Dauer selbst wird auf 8 ms gerundet, man sollte also keine Nachkommastellen interpretieren.
Jeden Teil gezielt verkleinern
- Eingabeverzögerung: lange Tasks aufteilen (siehe Lange Tasks finden, bevor es die Nutzer tun), besonders während des Ladens, wenn Skripte ausgewertet und Komponenten initialisiert werden.
- Verarbeitung: im Handler nur das tun, was für die sichtbare Reaktion nötig ist, und den Rest verschieben.
- Darstellung: weniger DOM auf einmal ändern, Layout Thrashing vermeiden (siehe Erst lesen, dann schreiben: Layout Thrashing vermeiden) und große, unsichtbare Bereiche mit
content-visibility: autoaus dem Rendering nehmen.
Für den mittleren Punkt hat sich ein einfaches Muster bewährt – erst die Rückmeldung, dann die Arbeit:
button.addEventListener('click', async () => {
button.classList.add('busy'); // sichtbare Reaktion sofort
await yieldToMain(); // jetzt darf der Browser ein Bild zeichnen
const result = compute(); // der teure Teil danach
render(result);
});
yieldToMain() ist dieselbe kleine Funktion wie in der Notiz über lange Tasks. Für INP endet die Interaktion mit dem ersten Bild, in dem der Button als beschäftigt erscheint. Die Berechnung danach zählt nicht mehr zu dieser Interaktion – sie verzögert aber natürlich die nächste, wenn sie selbst ein langer Task ist.