Identyfikowanie przyczyn słabego wyniku INP

Słabe reagowanie na interakcje sprawia, że strona wydaje się powolna lub nawet całkowicie uszkodzona. Interakcja do kolejnego wyrenderowania (INP) to wskaźnik oparty na interfejsie Event Timing API. Mierzy najgorszą interakcję (minus niektóre wartości odstające) jako miarę responsywności strony.

Identyfikowanie głównych przyczyn braku odpowiedzi strony internetowej może być trudne, zwłaszcza że zależy to od interakcji użytkownika i warunków środowiskowych, takich jak możliwości urządzenia i warunki sieciowe. Utrudnia to diagnozowanie problemów w porównaniu z bardziej powtarzalnym i przewidywalnym scenariuszem, takim jak wczytywanie strony. Dane laboratoryjne odzwierciedlają tylko niewielką część rzeczywistych scenariuszy użytkowników, dlatego pomiar przyczyn powolnego INP w terenie jest niezbędny.

Interfejs Event Timing API umożliwia podzielenie czasu trwania INP na 3 części: opóźnienie wejściowe (przetwarzanie, które już trwa, gdy następuje interakcja), czas przetwarzania (opóźnienia wynikające bezpośrednio z interakcji) i opóźnienie prezentacji (opóźnienia wynikające z renderowania następnej klatki po interakcji). Dzięki temu możesz sprawdzić, czy problem występuje w innym kodzie, w kodzie JavaScriptu interakcji czy w procesie renderowania aktualizacji w przeglądarce, zamiast od razu przechodzić do kodu interakcji.

Można uzyskać więcej informacji o kodzie JavaScript, ponownym obliczaniu stylu i układzie, które opóźniają interakcję. Pełne śledzenie wydajności za pomocą interfejsu JS Self-Profiling API to rozwiązanie, które może powodować problemy z wydajnością. Interfejs Long Animation Frames API to lekki interfejs API, którego można używać w terenie do identyfikowania wolno działającego kodu JavaScript oraz rozróżniania kosztów ponownego obliczania stylu i układu podczas interakcji INP:

  • Wymuszony styl i układ na poziomie skryptu (forcedStyleDuration i forcedLayoutDuration): raportuje synchroniczne ponowne obliczenia stylu (np. wywołanie getComputedStyle() po mutacjach DOM) i wymuszone układy/ponowne renderowanie (np. odczyt getBoundingClientRect() lub offsetWidth po mutacjach) w uruchomionym skrypcie podczas opóźnienia wejściowego lub czasu przetwarzania.
  • Styl i układ renderowania na poziomie klatki (styleDuration i layoutDuration): raportuje czas spędzony na ponownym obliczaniu stylów w porównaniu z czasem aktualizowania układu podczas aktualizacji renderowania klatki, co pomaga wyjaśnić wysokie opóźnienie prezentacji.

Odróżnienie ponownego obliczania stylu od układu jest kluczowe, ponieważ mają one różne główne przyczyny i rozwiązania: długi czas obliczania stylu wskazuje na kosztowne dopasowywanie selektorów CSS lub szerokie unieważnianie stylu (rozwiązaniem jest uproszczenie selektorów lub ograniczenie zakresu reguł), natomiast długi czas obliczania układu wskazuje na kosztowne obliczenia geometryczne lub synchroniczne szamotanie układu (rozwiązaniem jest grupowanie odczytów DOM przed zapisami, uproszczenie struktury DOM lub użycie kontenera CSS).

Jak to zrobić?

Obliczanie INP na podstawie nieprzetworzonych danych z interfejsu Event Timing API jest skomplikowane i ma kilka niuansów. Do zbierania tych danych zalecamy używanie narzędzia lub biblioteki RUM.

web-vitals to biblioteka open source od Google, która oblicza Core Web Vitals, w tym INP, a także zawiera podczęści i dane o długich klatkach animacji dotyczące interakcji INP.

Uzyskiwanie atrybucji interakcji INP za pomocą biblioteki web-vitals

Biblioteka web-vitals to niewielka biblioteka służąca do pomiaru podstawowych wskaźników internetowych i innych wskaźników wydajności. Funkcja onINP() może służyć do identyfikowania najwolniejszej interakcji i zawiera informacje o podczęściach INP, skryptach, które zostały wykonane podczas interakcji, oraz czasie trwania stylu i układu przy użyciu interfejsu API długich klatek animacji.

// Use the attribution build to get Long Animation Frame data
// alongside the INP metric value.
import { onINP } from 'web-vitals/attribution';

onINP((metric) => {
  const { attribution } = metric;
  const longestScript = attribution.longestScript;

  // Sum rendering-phase style and layout durations across all LoAF entries
  // intersecting the INP interaction (primarily explains presentationDelay).
  let renderStyleDuration = 0;
  let renderLayoutDuration = 0;
  for (const entry of attribution.longAnimationFrameEntries) {
    renderStyleDuration += entry.styleDuration ?? 0;
    renderLayoutDuration += entry.layoutDuration ?? 0;
  }

  // Beacon script, style, and layout attribution for the INP interaction,
  // so you can identify the root cause in production.
  navigator.sendBeacon(
    '/analytics',
    JSON.stringify({
      name: 'INP',
      value: metric.value,
      // These fields give the INP subparts:
      inputDelay: attribution.inputDelay,
      presentationDelay: attribution.presentationDelay,
      processingDuration: attribution.processingDuration,
      interactionTarget: attribution.interactionTarget,
      // These fields identify which script function was responsible
      // for the longest processing during the INP interaction.
      invokerType: longestScript?.entry?.invokerType,
      sourceURL: longestScript?.entry?.sourceURL,
      sourceFunctionName: longestScript?.entry?.sourceFunctionName,
      sourceCharPosition: longestScript?.entry?.sourceCharPosition,
      // subpart indicates which phase (input delay, processing, or
      // presentation delay) the longest script overlapped with most.
      subpart: longestScript?.subpart,
      intersectingDuration: longestScript?.intersectingDuration,
      // Synchronous style and layout work forced inside the longest script
      // (with forcedStyleAndLayoutDuration as a combined fallback):
      forcedStyleDuration: longestScript?.entry?.forcedStyleDuration,
      forcedLayoutDuration: longestScript?.entry?.forcedLayoutDuration,
      forcedStyleAndLayoutDuration: longestScript?.entry?.forcedStyleAndLayoutDuration,
      // Rendering-phase style and layout work across the interaction's frames
      // (with totalStyleAndLayoutDuration as a combined fallback):
      renderStyleDuration,
      renderLayoutDuration,
      totalStyleAndLayoutDuration: attribution.totalStyleAndLayoutDuration
    })
  );
});

Sprawdzone metody

  • UŻYWAJ na początku podczęści INP, aby określić, czy opóźnienie jest spowodowane już uruchomionym kodem JavaScript (opóźnienie wejściowe), modułami obsługi zdarzeń JavaScript dla interakcji (czas przetwarzania) czy późniejszym renderowaniem (opóźnienie prezentacji).
  • SPRÓBUJ zidentyfikować największy blokujący kod JavaScript jako dodatkowy szczegół, zwłaszcza w przypadku opóźnienia działania i czasu przetwarzania.
  • ODRÓŻNIAJ ponowne obliczanie stylu (styleDuration, forcedStyleDuration) od układu (layoutDuration, forcedLayoutDuration) podczas diagnozowania długiego czasu przetwarzania lub opóźnienia prezentacji: długi czas przetwarzania stylu wskazuje na kosztowne selektory CSS lub szerokie unieważnienia, natomiast długi czas przetwarzania układu wskazuje na wąskie gardła geometrii DOM lub synchroniczne niszczenie układu.
  • ZALECAMY używanie interfejsu Long Animation Frames API do dostarczania tych dodatkowych informacji zamiast alternatywnych rozwiązań, takich jak JS Self-Profiling API, które wiążą się z większym obciążeniem w czasie działania.
  • UŻYWAJ biblioteki web-vitals, jeśli nie masz innego rozwiązania do monitorowania rzeczywistych użytkowników. Może on identyfikować interakcję INP i zawiera informacje o jej elementach składowych oraz skryptach, które zostały wykonane podczas interakcji (za pomocą interfejsu Long Animation Frames API).
  • DO beacon back the required information to an analytics service rather than just log it locally.

Obsługa przeglądarek i strategie zastępcze

Interfejs Event Timing API jest dostępny w większości nowoczesnych przeglądarek i jest niezbędny do obliczania INP oraz jego elementów podrzędnych. W przypadku przeglądarek, które nie obsługują tego interfejsu API, nie można zmierzyć INP.

Interfejs Long Animation Frames API udostępnia opcjonalne dodatkowe informacje. Można go bezpiecznie używać bez kreacji zastępczych. W większości przypadków możliwości poprawy wydajności, które zidentyfikuje, będą dotyczyć również innych przeglądarek.

Jeśli Baseline obsługuje interfejs Long Animation Frames API (long-animation-frames), ale nie obsługuje osobnych właściwości czasu trwania stylu i układu (styleDuration i layoutDuration w PerformanceLongAnimationFrameTiming lub forcedStyleDuration i forcedLayoutDuration w PerformanceScriptTiming), użyj połączonych czasów stylu i układu:

  • Używaj script.forcedStyleAndLayoutDuration w PerformanceScriptTiming do synchronicznego wymuszania stylu i układu w skryptach.
  • Użyj metric.attribution.totalStyleAndLayoutDuration w web-vitals/attribution (lub oblicz (entry.paintTime || entry.startTime + entry.duration) - entry.styleAndLayoutStart dla każdego wpisu PerformanceLongAnimationFrameTiming), aby połączyć styl i układ.