Znajdowanie powolnych interakcji w terenie

Dowiedz się, jak znaleźć w danych polowych witryny powolne interakcje, aby móc znaleźć możliwości poprawy czasu od interakcji do kolejnego wyrenderowania.

Dane z pola to dane, które informują, jak użytkownicy korzystają z Twojej witryny. Wykrywa problemy, których nie można znaleźć tylko w danych laboratoryjnych. W przypadku interakcji do kolejnego wyrenderowania (INP) dane w polu są niezbędne do identyfikowania wolnych interakcji i zawierają ważne wskazówki, które pomogą Ci je rozwiązać.

Z tego przewodnika dowiesz się, jak szybko ocenić INP witryny za pomocą danych z pól w Raporcie na temat użytkowania Chrome (CrUX), aby sprawdzić, czy Twoja witryna ma problemy z tą metryką. Następnie dowiesz się, jak używać wersji atrybucji biblioteki JavaScript web-vitals oraz nowych statystyk, które udostępnia ona dzięki interfejsowi Long Animation Frames API (LoAF), do zbierania i interpretowania danych polowych dotyczących powolnych interakcji w Twojej witrynie.

Zacznij od CrUX, aby ocenić wartość INP swojej witryny

Jeśli nie zbierasz danych zgromadzonych od użytkowników witryny, możesz zacząć od raportu CrUX. Raport CrUX zbiera dane z pól od prawdziwych użytkowników Chrome, którzy zgodzili się na wysyłanie danych telemetrycznych.

Dane raportu na temat użytkowania Chrome wyświetlają się w wielu różnych obszarach i zależą od zakresu informacji, których szukasz. Narzędzie CrUX może udostępniać dane o INP i innych podstawowych wskaźnikach internetowych w przypadku:

  • pojedyncze strony i całe źródła za pomocą PageSpeed Insights;
  • Typy stron. Na przykład wiele witryn e-commerce ma strony typu „Strona szczegółów produktu” i „Strona z informacjami o produkcie”. W Search Console możesz uzyskać dane raportu na temat użytkowania Chrome dotyczące unikalnych typów stron.

Na początek możesz wpisać adres URL swojej witryny w PageSpeed Insights. Gdy wpiszesz adres URL, dane z jego pola (jeśli są dostępne) będą wyświetlać się dla wielu wskaźników, w tym INP. Możesz też użyć przełączników, aby sprawdzić wartości INP dla wymiarów na urządzeniach mobilnych i komputerach.

Dane pól wyświetlane przez CrUX w Statystykach PageSpeed, pokazujące LCP, INP, CLS w przypadku 3 podstawowych wskaźników internetowych oraz TTFB, FCP jako dane diagnostyczne i FID jako wycofane podstawowe wskaźniki internetowe.
Odczyt danych raportu CrUX na podstawie statystyk PageSpeed. W tym przykładzie INP danej strony internetowej wymaga poprawy.

Te dane są przydatne, ponieważ informują o problemach. Jednak raport na temat użytkowania Chrome nie może przekazać informacji, co jest przyczyną problemów. Dostępnych jest wiele rozwiązań do monitorowania rzeczywistych użytkowników (RUM), które pomogą Ci zbierać własne dane z pól użytkowników witryny. Dzięki temu możesz uzyskać odpowiedź na to pytanie. Jedną z opcji jest samodzielne zbieranie tych danych za pomocą biblioteki JavaScript web-vitals.

Zbieraj dane pól za pomocą biblioteki JavaScript web-vitals

Biblioteka JavaScript web-vitals to skrypt, który możesz wczytać w swojej witrynie, by zbierać dane od jej użytkowników. Możesz go używać do rejestrowania różnych wskaźników, w tym INP, w przeglądarkach, które go obsługują.

Obsługa przeglądarek

  • Chrome: 96.
  • Krawędź: 96.
  • Firefox: nieobsługiwane.
  • Safari: nieobsługiwane.

Źródło

Standardowa wersja biblioteki web-vitals może służyć do uzyskiwania podstawowych danych INP od użytkowników w polu:

import {onINP} from 'web-vitals';

onINP(({name, value, rating}) => {
  console.log(name);    // 'INP'
  console.log(value);   // 512
  console.log(rating);  // 'poor'
});

Aby przeanalizować dane z pola, które otrzymujesz od użytkowników, musisz je gdzieś wysłać:

import {onINP} from 'web-vitals';

onINP(({name, value, rating}) => {
  // Prepare JSON to be sent for collection. Note that
  // you can add anything else you'd want to collect here:
  const body = JSON.stringify({name, value, rating});

  // Use `sendBeacon` to send data to an analytics endpoint.
  // For Google Analytics, see https://github.com/GoogleChrome/web-vitals#send-the-results-to-google-analytics.
  navigator.sendBeacon('/analytics', body);
});

Jednak te dane same w sobie nie dają Ci więcej informacji niż CrUX. Właśnie do tego służy wersja atrybucji biblioteki web-vitals.

Pójdź o krok dalej dzięki tworzeniu atrybucji w bibliotece Web Vitals

Moduł atrybucji w bibliotece web-vitals udostępnia dodatkowe dane, które możesz uzyskać od użytkowników, aby ułatwić rozwiązywanie problemów z interakcjami, które wpływają na INP witryny. Te dane są dostępne za pomocą obiektu attribution wyświetlanego w metodzie onINP() biblioteki:

import {onINP} from 'web-vitals/attribution';

onINP(({name, value, rating, attribution}) => {
  console.log(name);         // 'INP'
  console.log(value);        // 56
  console.log(rating);       // 'good'
  console.log(attribution);  // Attribution data object
});
Jak wyglądają logi konsoli z biblioteki web-vitals W tym przykładzie konsola pokazuje nazwę wskaźnika (INP), wartość INP (56), która mieści się w progach INP (dobrze), oraz różne informacje wyświetlane w obiekcie atrybucji, w tym wpisy z interfejsu Long Animation Frames API.
Jak dane z biblioteki Web Vitals są wyświetlane w konsoli.

Oprócz INP strony kompilacja atrybucji zawiera też wiele danych, które pomogą Ci zrozumieć przyczyny powolnego działania interakcji, m.in. na której części interakcji powinieneś się skupić. Pomaga ono odpowiadać na ważne pytania, np.:

  • „Czy użytkownik wchodził w interakcję ze stroną podczas jej wczytywania?”
  • „Czy przetwarzanie zdarzeń interakcji trwało długo?”
  • „Czy kod obsługi interakcji został opóźniony?” If so, what else was happening on the main thread at that time?"
  • „Czy interakcja spowodowała dużo prac związanych z renderowaniem, które opóźniły wymalowanie następnej klatki?”

W tabeli poniżej znajdziesz podstawowe dane atrybucji, które możesz uzyskać z biblioteki. Mogą one pomóc Ci w określeniu ogólnych przyczyn powolnych interakcji w Twojej witrynie:

Klucz obiektu attribution Dane
interactionTarget Selektor arkusza CSS wskazujący element, który wygenerował wartość INP strony, np. button#save.
interactionType Typ interakcji: kliknięcia, dotknięcia lub wpisy na klawiaturze.
inputDelay* Opóźnienie wejściowe interakcji.
processingDuration* Czas od momentu, gdy pierwszy odbiorca zdarzeń zaczął działać w odpowiedzi na interakcję użytkownika, do momentu zakończenia przetwarzania odbiornika zdarzeń.
presentationDelay* Opóźnienie wyświetlania interakcji, które występuje od momentu zakończenia działania obsługi zdarzeń do momentu wyświetlenia następnego kadru.
longAnimationFrameEntries* Wpisy z LoAF powiązane z interakcją. Więcej informacji znajdziesz w następnej sekcji.
*Nowość w wersji 4

Od wersji 4 biblioteki Web Vitals możesz uzyskać jeszcze pełniejszy wgląd w problematyczne interakcje dzięki raportom, które zawierają informacje o fazach INP (opóźnienie wejściowe, czas przetwarzania i opóźnienie prezentacji) oraz interfejs Long Animation Frames API (LoAF).

Long Animation Frames API (LoAF)

Obsługa przeglądarek

  • Chrome: 123.
  • Edge: 123.
  • Firefox: funkcja nieobsługiwana.
  • Safari: nieobsługiwane.

Źródło

Debugowanie interakcji na podstawie danych z terenu nie jest łatwym zadaniem. Jednak dzięki danym z LoAF można teraz uzyskać lepszy wgląd w przyczyny powolnych interakcji, ponieważ udostępnia ono szereg szczegółowych kodów czasowych i innych danych, które pozwalają zidentyfikować precyzyjne przyczyny, a co ważniejsze – umiejscowienie źródła problemu w kodzie witryny.

Wersja atrybucji biblioteki web-vitals udostępnia tablicę wpisów LoAF pod kluczem longAnimationFrameEntries obiektu attribution. W tabeli poniżej znajdziesz kilka kluczowych informacji, które możesz znaleźć w każdym wpisie LoAF:

Klucz obiektu wpisu LoAF Dane
duration Czas trwania długiej klatki animacji do momentu ukończenia układu, z wyłączeniem malowania i komponowania.
blockingDuration Łączny czas w ramach interwału, w którym przeglądarka nie mogła szybko odpowiadać z powodu długich zadań. Ten czas blokowania może obejmować długie zadania wykonywane przez JavaScript, a także wszelkie kolejne długie zadania renderowania w ramach danego interwału.
firstUIEventTimestamp Sygnatura czasowa określająca, kiedy zdarzenie zostało umieszczone w kolejce w ramach ramki. Przydatne do określenia opóźnienia wejścia na początku interakcji.
startTime Sygnatura czasowa rozpoczęcia klatki.
renderStart Data rozpoczęcia renderowania klatki. Obejmuje to wszystkie wywołania requestAnimationFrame (a także wywołania ResizeObserver, jeśli to konieczne), ale przed rozpoczęciem pracy nad stylem lub układem.
styleAndLayoutStart Gdy w ramce występują zmiany stylu lub układu. Może być przydatne do określenia długości pracy nad stylem lub układem, gdy uwzględnisz inne dostępne sygnatury czasowe.
scripts Tablica elementów zawierających informacje o atrybucji skryptów przyczyniające się do wartości INP strony.
Wizualizacja długiej klatki animacji zgodnie z modelem LoAF.
Diagram przedstawiający czas trwania długiej klatki animacji zgodnie z interfejsem LoAF API (z wyjątkiem blockingDuration).

Wszystkie te informacje mogą wiele powiedzieć o tym, co powoduje spowolnienie interakcji, ale szczególne znaczenie mają tablice scripts, które pojawiają się w rekordach LoAF:

Klucz obiektu atrybucji skryptu Dane
invoker Wywołujący. Może się to różnić w zależności od typu wywołującego opisanego w kolejnym wierszu. Przykładami elementów wywołujących mogą być wartości takie jak 'IMG#id.onload', 'Window.requestAnimationFrame' czy 'Response.json.then'.
invokerType Typ wywołującego. Może to być 'user-callback', 'event-listener', 'resolve-promise', 'reject-promise', 'classic-script' lub 'module-script'.
sourceURL Adres URL skryptu, z którego pochodzi długa klatka animacji.
sourceCharPosition Pozycja znaku w skrypcie, którą wskazuje sourceURL.
sourceFunctionName Nazwa funkcji w wykrytym skrypcie.

Każdy wpis w tym tablicy zawiera dane widoczne w tej tabeli, które dostarczają informacji o skrypcie odpowiedzialnym za wolne działanie interakcji oraz o przyczynie tego stanu.

Pomiar i identyfikowanie typowych przyczyn powolnego działania

Z tego przewodnika dowiesz się, jak możesz wykorzystać te informacje. Z tego przewodnika dowiesz się, jak korzystać z danych LoAF wyświetlanych w bibliotece web-vitals, aby znaleźć niektóre przyczyny powolnych interakcji.

Długi czas przetwarzania

Czas przetwarzania interakcji to czas, jaki zajmuje wykonanie zarejestrowanych wywołań zwrotnych modułu obsługi zdarzeń, oraz wszystko, co może się zdarzyć między nimi. Biblioteka web-vitals wyświetla długi czas przetwarzania:

import {onINP} from 'web-vitals/attribution';

onINP(({name, value, attribution}) => {
  const {processingDuration} = attribution; // 512.5
});

Naturalnie można pomyśleć, że główną przyczyną powolnego działania jest zbyt długi czas wykonywania kodu w obiekcie obsługującym zdarzenie, ale nie zawsze tak jest. Gdy potwierdzisz, że na tym polega problem, możesz skorzystać z bardziej szczegółowych danych dotyczących LoAF:

import {onINP} from 'web-vitals/attribution';

onINP(({name, value, attribution}) => {
  const {processingDuration} = attribution; // 512.5

  // Get the longest script from LoAF covering `processingDuration`:
  const loaf = attribution.longAnimationFrameEntries.at(-1);
  const script = loaf?.scripts.sort((a, b) => b.duration - a.duration)[0];

  if (script) {
    // Get attribution for the long-running event handler:
    const {invokerType} = script;        // 'event-listener'
    const {invoker} = script;            // 'BUTTON#update.onclick'
    const {sourceURL} = script;          // 'https://example.com/app.js'
    const {sourceCharPosition} = script; // 83
    const {sourceFunctionName} = script; // 'update'
  }
});

Jak widać w poprzednim fragmencie kodu, możesz korzystać z danych LoAF, aby ustalić dokładną przyczynę interakcji z wysokimi wartościami czasu przetwarzania, m.in.:

  • Element i zarejestrowany detektor zdarzeń.
  • Plik skryptu (oraz pozycja znaku w nim) zawierający długotrwały kod obsługi zdarzenia.
  • Nazwa funkcji.

Te dane są bezcenne. Nie musisz już dowiadywać się, która interakcja lub który z obejmujących ją event handlerów odpowiada za długi czas przetwarzania. Ponieważ skrypty innych firm często mogą rejestrować własne moduły obsługi zdarzeń, możesz określić, czy to Twój kod był odpowiedzialny za problem. W przypadku kodu, nad którym masz kontrolę, warto zoptymalizować długie zadania.

Duże opóźnienia danych wejściowych

Chociaż długotrwałe metody obsługi zdarzeń są powszechne, należy wziąć pod uwagę inne części interakcji. Jedna część występuje przed przetwarzaniem, co nazywamy opóźnieniem wejścia. Jest to czas od zainicjowania przez użytkownika interakcji do momentu rozpoczęcia wywoływania jego wywołań zwrotnych modułu obsługi zdarzeń i ma miejsce, gdy wątek główny przetwarza już inne zadanie. Dzięki budowie atrybucji biblioteki web-vitals możesz sprawdzić czas opóźnienia wprowadzania danych w przypadku interakcji:

import {onINP} from 'web-vitals/attribution';

onINP(({name, value, attribution}) => {
  const {inputDelay} = attribution; // 125.59439536
});

Jeśli zauważysz, że niektóre interakcje mają długie opóźnienia w wejściu, musisz ustalić, co działo się na stronie w momencie interakcji, która spowodowała długi czas oczekiwania na dane wejściowe. Często zależy to od tego, czy interakcja miała miejsce podczas wczytywania strony, czy później.

Czy wystąpiło to podczas wczytywania strony?

Wątek główny jest często najbardziej obciążony podczas wczytywania strony. W tym czasie wszystkie zadania są umieszczane w kolejce i przetwarzane. Jeśli użytkownik spróbuje wejść na stronę, gdy trwa to działanie, może to opóźnić interakcję. Strony, które wczytują dużo kodu JavaScript, mogą inicjować kompilację i sprawdzanie skryptów, a także wykonywać funkcje, które przygotowują stronę do interakcji z użytkownikiem. Może to przeszkadzać, jeśli użytkownik wejdzie w interakcję z treścią w trakcie wykonywania tej czynności. Możesz sprawdzić, czy tak się dzieje w przypadku użytkowników Twojej witryny:

import {onINP} from 'web-vitals/attribution';

onINP(({name, value, attribution}) => {
  const {inputDelay} = attribution; // 125.59439536

  // Get the longest script from the first LoAF entry:
  const loaf = attribution.longAnimationFrameEntries[0];
  const script = loaf?.scripts.sort((a, b) => b.duration - a.duration)[0];

  if (script) {
    // Invoker types can describe if script eval blocked the main thread:
    const {invokerType} = script;    // 'classic-script' | 'module-script'
    const {sourceLocation} = script; // 'https://example.com/app.js'
  }
});

Jeśli podczas rejestrowania tych danych w polu widzisz długie opóźnienia w składaniu danych i typy wywoływacza 'classic-script' lub 'module-script', można założyć, że skrypty na stronie długo się przetwarzają i blokują główny wątek na tyle długo, że opóźniają interakcje. Czas blokowania możesz skrócić, dzieląc skrypty na mniejsze pakiety, odkładając początkowo nieużywany kod do późniejszego wczytania oraz sprawdzając, czy w witrynie nie ma nieużywanego kodu, który można całkowicie usunąć.

Czy to nastąpiło po załadowaniu strony?

Chociaż opóźnienia w działaniu klawiatury często występują podczas wczytywania strony, mogą też występować po jej wczytaniu z całkowicie innych przyczyn. Typowymi przyczynami opóźnień wprowadzania danych po załadowaniu strony może być kod, który jest uruchamiany okresowo z powodu wcześniejszego wywołania funkcji setInterval, a nawet wywołania zwrotne zdarzeń, które zostały umieszczone w kole do uruchomienia wcześniej i są nadal przetwarzane.

import {onINP} from 'web-vitals/attribution';

onINP(({name, value, attribution}) => {
  const {inputDelay} = attribution; // 125.59439536

  // Get the longest script from the first LoAF entry:
  const loaf = attribution.longAnimationFrameEntries[0];
  const script = loaf?.scripts.sort((a, b) => b.duration - a.duration)[0];

  if (script) {
    const {invokerType} = script;        // 'user-callback'
    const {sourceURL} = script;          // 'https://example.com/app.js'
    const {sourceCharPosition} = script; // 83
    const {sourceFunctionName} = script; // 'update'
  }
});

Podobnie jak w przypadku rozwiązywania problemów z wysokimi wartościami czasu przetwarzania, długie opóźnienia w przyjmowaniu danych spowodowane wymienionymi wcześniej przyczynami pozwolą Ci uzyskać szczegółowe dane o przypisaniu skryptu. Różnica polega na tym, że typ wywołującego będzie się zmieniać w zależności od charakteru pracy, która opóźniła interakcję:

  • 'user-callback' oznacza, że zadanie blokowania pochodzi z setInterval, setTimeout, a nawet requestAnimationFrame.
  • 'event-listener' oznacza, że zadanie blokowania pochodzi z wcześniejszych danych wejściowych, które znajdują się w kolejce i nadal są przetwarzane.
  • 'resolve-promise''reject-promise' oznaczają, że zadanie blokujące pochodziło z niektórych asynchronicznych działań, które zostały rozpoczęte wcześniej i rozwiązane lub odrzucone w momencie, gdy użytkownik próbował wejść w interakcję ze stroną, opóźniając ją.

W każdym razie dane atrybucji skryptu pomogą Ci określić, od czego zacząć szukać problemu i czy opóźnienie w wprowadzaniu danych było spowodowane Twoim kodem czy skryptem innej firmy.

Długie opóźnienia prezentacji

Opóźnienia wyświetlania to ostatni etap interakcji. Rozpoczynają się one po zakończeniu działania obsługi zdarzeń interakcji i trwają do momentu wyświetlenia następnego klatki. Występują one, gdy działanie w obiekcie event handler z powodu interakcji zmienia wizualny stan interfejsu użytkownika. Podobnie jak w przypadku czasu przetwarzania i opóźnień wprowadzania danych, biblioteka web-vitals może poinformować, jak długie było opóźnienie wyświetlania w przypadku danej interakcji:

import {onINP} from 'web-vitals/attribution';

onINP(({name, value, attribution}) => {
  const {presentationDelay} = attribution; // 113.32307691
});

Jeśli rejestrujesz te dane i obserwujesz duże opóźnienia w prezentowaniu interakcji, które przyczyniają się do wzrostu INP witryny, przyczyny mogą być różne, ale warto zwrócić uwagę na kilka z nich.

kosztowne prace nad stylem i układem;

Długie opóźnienia w prezentowaniu mogą być spowodowane kosztownym przeliczaniem stylówukładem, które wynikają z różnych przyczyn, w tym złożonych selektorów CSS i dużych rozmiarów DOM. Możesz mierzyć czas trwania tej pracy za pomocą kodów czasowych LoAF wyświetlanych w bibliotece Web Vitals:

import {onINP} from 'web-vitals/attribution';

onINP(({name, value, attribution}) => {
  const {presentationDelay} = attribution; // 113.32307691

  // Get the longest script from the last LoAF entry:
  const loaf = attribution.longAnimationFrameEntries.at(-1);
  const script = loaf?.scripts.sort((a, b) => b.duration - a.duration)[0];

  // Get necessary timings:
  const {startTime} = loaf; // 2120.5
  const {duration} = loaf;  // 1002

  // Figure out the ending timestamp of the frame (approximate):
  const endTime = startTime + duration; // 3122.5

  // Get the start timestamp of the frame's style/layout work:
  const {styleAndLayoutStart} = loaf; // 3011.17692309

  // Calculate the total style/layout duration:
  const styleLayoutDuration = endTime - styleAndLayoutStart; // 111.32307691

  if (script) {
    // Get attribution for the event handler that triggered
    // the long-running style and layout operation:
    const {invokerType} = script;        // 'event-listener'
    const {invoker} = script;            // 'BUTTON#update.onclick'
    const {sourceURL} = script;          // 'https://example.com/app.js'
    const {sourceCharPosition} = script; // 83
    const {sourceFunctionName} = script; // 'update'
  }
});

LoAF nie podaje czasu trwania pracy nad stylem i układem w przypadku danej klatki, ale informuje, kiedy rozpoczęła się ta praca. Na podstawie tego sygnatury czasowej początkowej możesz użyć innych danych z LoAF, aby obliczyć dokładny czas trwania tego zadania. W tym celu określ czas zakończenia ramki i odejmij od niego sygnaturę czasową początkową zadania związanego ze stylem i układem.

Długotrwałe requestAnimationFramewywołania zwrotne

Jedną z potencjalnych przyczyn długich opóźnień w prezentacji może być nadmierna ilość pracy wykonywanej w ramach wywołania zwrotnego requestAnimationFrame. Treść tej funkcji zwracanej jest wykonywana po zakończeniu działania obsługi zdarzeń, ale tuż przed ponownym obliczeniem stylu i ułożeniem.

Jeśli wykonywane w nich zadania są złożone, wywołania te mogą zająć sporo czasu. Jeśli podejrzewasz, że wysokie wartości opóźnienia prezentacji są spowodowane pracą nad requestAnimationFrame, możesz użyć danych LoAF wyświetlanych przez bibliotekę web-vitals, aby zidentyfikować te scenariusze:

onINP(({name, value, attribution}) => {
  const {presentationDelay} = attribution; // 543.1999999880791

  // Get the longest script from the last LoAF entry:
  const loaf = attribution.longAnimationFrameEntries.at(-1);
  const script = loaf?.scripts.sort((a, b) => b.duration - a.duration)[0];

  // Get the render start time and when style and layout began:
  const {renderStart} = loaf;         // 2489
  const {styleAndLayoutStart} = loaf; // 2989.5999999940395

  // Calculate the `requestAnimationFrame` callback's duration:
  const rafDuration = styleAndLayoutStart - renderStart; // 500.59999999403954

  if (script) {
    // Get attribution for the event handler that triggered
    // the long-running requestAnimationFrame callback:
    const {invokerType} = script;        // 'user-callback'
    const {invoker} = script;            // 'FrameRequestCallback'
    const {sourceURL} = script;          // 'https://example.com/app.js'
    const {sourceCharPosition} = script; // 83
    const {sourceFunctionName} = script; // 'update'
  }
});

Jeśli zauważysz, że znaczna część czasu opóźnienia prezentacji jest wykonywana w wywołaniu zwrotnym requestAnimationFrame, upewnij się, że Twoja praca w tych wywołaniach jest ograniczona do czynności, które powodują aktualizację interfejsu użytkownika. Inne działania, które nie wpływają na DOM ani nie aktualizują stylów, niepotrzebnie opóźnią wyświetlanie kolejnego klatki, więc bądź ostrożny.

Podsumowanie

Dane z pola to najlepsze źródło informacji, z którego możesz korzystać, aby dowiedzieć się, które interakcje są problematyczne dla rzeczywistych użytkowników. Korzystając z narzędzi do zbierania danych w terenie, takich jak biblioteka JavaScript Web Vitals (lub dostawca RUM), możesz mieć większą pewność co do tego, które interakcje są najbardziej problematyczne, a następnie przejść do odtwarzania problematycznych interakcji w module, a potem zająć się ich wyeliminowaniem.

Baner powitalny z Unsplash: Federico Respini.