Dowiedz się, jak znaleźć powolne interakcje w danych z pola witryny, aby znaleźć możliwości poprawy czasu od interakcji do kolejnego wyrenderowania.
Dane z testów w terenie to dane, które pokazują, jak prawdziwi użytkownicy korzystają z Twojej witryny. Pozwala wykryć problemy, których nie można znaleźć w samych danych laboratoryjnych. Jeśli chodzi o interakcję do kolejnego wyrenderowania (INP), dane zgromadzone po stronie użytkownika są niezbędne do identyfikowania powolnych interakcji i zawierają ważne wskazówki, które pomogą Ci je naprawić.
Z tego przewodnika dowiesz się, jak szybko ocenić INP witryny, korzystając z danych z pól z Raportu na temat użytkowania Chrome (CrUX), aby sprawdzić, czy witryna ma problemy z INP. Następnie dowiesz się, jak używać kompilacji atrybucji biblioteki JavaScript web-vitals i nowych statystyk, które udostępnia ona z interfejsu Long Animation Frames API (LoAF), aby zbierać i interpretować dane z pola dotyczące powolnych interakcji w witrynie.
Zacznij od CrUX, aby ocenić INP witryny
Jeśli nie zbierasz danych zgromadzonych od użytkowników witryny, raport CrUX może być dobrym punktem wyjścia. CrUX zbiera dane z pól od prawdziwych użytkowników Chrome, którzy zgodzili się na wysyłanie danych telemetrycznych.
Dane CrUX są wyświetlane w różnych miejscach, w zależności od zakresu informacji, których szukasz. CrUX może dostarczać dane o INP i innych Core Web Vitals w przypadku:
- poszczególne strony i całe źródła za pomocą PageSpeed Insights;
- Typy stron. Na przykład wiele witryn e-commerce zawiera strony ze szczegółami produktu i strony z listą produktów. Dane CrUX dotyczące unikalnych typów stron możesz uzyskać w Search Console.
Na początek możesz wpisać adres URL swojej witryny w PageSpeed Insights. Gdy wpiszesz adres URL, zostaną wyświetlone dane zgromadzone dla niego (jeśli są dostępne) w przypadku 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.
Te dane są przydatne, ponieważ informują o problemie. CrUX nie może jednak wskazać, co powoduje problemy. Dostępnych jest wiele rozwiązań do monitorowania rzeczywistych użytkowników (RUM), które pomogą Ci zebrać własne dane z pól od użytkowników witryny, aby odpowiedzieć na to pytanie. Jedną z opcji jest samodzielne zbieranie danych z pól za pomocą biblioteki JavaScript web-vitals.
Zbieranie danych zgromadzonych przez użytkowników za pomocą biblioteki JavaScript web-vitals
Biblioteka JavaScriptu web-vitals to skrypt, który możesz wczytać w swojej witrynie, aby zbierać dane zgromadzone od jej użytkowników. Możesz go używać do rejestrowania wielu danych, w tym INP w przeglądarkach, które go obsługują.
Standardowa kompilacja biblioteki web-vitals może służyć do uzyskiwania podstawowych danych INP od użytkowników w terenie:
import {onINP} from 'web-vitals';
onINP(({name, value, rating}) => {
console.log(name); // 'INP'
console.log(value); // 512
console.log(rating); // 'poor'
});
Aby przeanalizować dane zgromadzone 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);
});
Te dane same w sobie nie mówią jednak dużo więcej niż CrUX. Właśnie do tego służy kompilacja atrybucji biblioteki web-vitals.
Więcej możliwości dzięki kompilacji atrybucji biblioteki web-vitals
Kompilacja atrybucji biblioteki web-vitals udostępnia dodatkowe dane, które możesz uzyskać od użytkowników w terenie, aby ułatwić rozwiązywanie problemów z interakcjami, które wpływają na INP Twojej witryny. Te dane są dostępne za pomocą obiektu attribution, który jest wyświetlany 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
});
Oprócz samego INP strony kompilacja atrybucji dostarcza wielu danych, które mogą pomóc w zrozumieniu przyczyn powolnych interakcji, w tym informacji o tym, na której części interakcji należy się skupić. Może pomóc Ci uzyskać odpowiedzi na ważne pytania, takie jak:
- „Czy użytkownik wszedł w interakcję ze stroną podczas jej wczytywania?”.
- „Czy moduły obsługi zdarzeń interakcji działały przez długi czas?”
- „Czy kod modułu obsługi zdarzeń interakcji został opóźniony? Jeśli tak, co jeszcze działo się w tym czasie w głównym wątku?”.
- „Czy interakcja spowodowała dużo pracy związanej z renderowaniem, co opóźniło wyrenderowanie następnej klatki?”.
W tabeli poniżej znajdziesz podstawowe dane o atrybucji, które możesz uzyskać z biblioteki. Mogą one pomóc Ci ustalić niektóre ogólne przyczyny powolnych interakcji w witrynie:
Klucz obiektu attribution
|
Dane |
|---|---|
interactionTarget
|
Selektor CSS wskazujący element, który wygenerował wartość INP strony, np. button#save.
|
interactionType
|
Typ interakcji, np. kliknięcia, dotknięcia lub wprowadzanie danych za pomocą klawiatury. |
inputDelay*
|
Opóźnienie wejściowe interakcji. |
processingDuration*
|
Czas od momentu, gdy pierwszy detektor zdarzeń zaczął działać w odpowiedzi na interakcję użytkownika, do momentu, gdy wszystkie detektory zdarzeń zakończyły przetwarzanie. |
presentationDelay*
|
Opóźnienie prezentacji interakcji, które występuje od momentu zakończenia działania modułów obsługi zdarzeń do momentu wyrenderowania następnej klatki. |
longAnimationFrameEntries*
|
Wpisy z LoAF powiązane z interakcją. Więcej informacji znajdziesz w następnej sekcji. |
Począwszy od wersji 4 biblioteki web-vitals, możesz uzyskać jeszcze dokładniejsze statystyki o problemach z interakcjami dzięki danym, które udostępnia ona w ramach zestawień faz INP (opóźnienie wejściowe, czas przetwarzania i opóźnienie prezentacji) oraz interfejsu API długich klatek animacji (LoAF).
Long Animation Frames API (LoAF)
Debugowanie interakcji przy użyciu danych z pola jest trudnym zadaniem. Dzięki danym z LoAF można jednak uzyskać lepszy wgląd w przyczyny powolnych interakcji, ponieważ LoAF udostępnia szereg szczegółowych danych o czasie i innych danych, których można użyć do określenia dokładnych przyczyn – a co ważniejsze, miejsca, w którym znajduje się źródło problemu w kodzie witryny.
Kompilacja 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żna znaleźć w każdym wpisie LoAF:
| Klucz obiektu wpisu LoAF | Dane |
|---|---|
duration
|
Czas trwania długiej klatki animacji do momentu zakończenia układu, ale bez uwzględniania renderowania i komponowania. |
blockingDuration
|
Łączny czas w ramce, przez który przeglądarka nie mogła szybko odpowiedzieć z powodu długich zadań. Ten czas blokowania może obejmować długie zadania, które uruchamiają kod JavaScript, a także wszelkie kolejne długie zadania renderowania w ramce. |
firstUIEventTimestamp
|
Sygnatura czasowa określająca, kiedy zdarzenie zostało dodane do kolejki podczas klatki. Przydatne do określania początku opóźnienia wejściowego interakcji. |
startTime
|
Sygnatura czasowa rozpoczęcia klatki. |
renderStart
|
Kiedy rozpoczęło się renderowanie klatki. Obejmuje to wszystkie wywołania zwrotne requestAnimationFrame (oraz wywołania zwrotne ResizeObserver, jeśli dotyczy), ale potencjalnie przed rozpoczęciem pracy nad stylem/układem.
|
styleAndLayoutStart
|
Kiedy w ramce występuje styl lub układ. Może być przydatne do określenia czasu pracy nad stylem/układem, gdy uwzględniane są inne dostępne sygnatury czasowe. |
scripts
|
Tablica elementów zawierających informacje o atrybucji skryptu, które wpływają na INP strony. |
blockingDuration).
Wszystkie te informacje mogą wiele powiedzieć o tym, co powoduje powolną interakcję, ale tablica scripts, którą wyświetlają wpisy LoAF, powinna być szczególnie interesująca:
| 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 następnym wierszu. Przykłady wywołujących to wartości takie jak 'IMG#id.onload', 'Window.requestAnimationFrame' lub 'Response.json.then'. |
invokerType
|
Typ elementu wywołującego. Możliwe wartości: '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 określona przez sourceURL.
|
sourceFunctionName
|
Nazwa funkcji w zidentyfikowanym skrypcie. |
Każdy wpis w tej tablicy zawiera dane widoczne w tej tabeli, które dostarczają informacji o skrypcie odpowiedzialnym za powolną interakcję i o tym, jak do tego doszło.
Mierzenie i identyfikowanie typowych przyczyn powolnych interakcji
Aby pokazać, jak możesz wykorzystać te informacje, w tym przewodniku omówimy, jak możesz użyć danych LoAF wyświetlanych w bibliotece web-vitals, aby określić przyczyny powolnych interakcji.
Długi czas przetwarzania
Czas przetwarzania interakcji to czas potrzebny na wykonanie zarejestrowanych wywołań zwrotnych modułu obsługi zdarzeń interakcji i wszystkich innych czynności, które mogą wystąpić między nimi. Długie czasy przetwarzania są wyświetlane przez bibliotekę web-vitals:
import {onINP} from 'web-vitals/attribution';
onINP(({name, value, attribution}) => {
const {processingDuration} = attribution; // 512.5
});
Można pomyśleć, że główną przyczyną powolnej interakcji jest zbyt długi czas wykonywania kodu modułu obsługi zdarzeń, ale nie zawsze tak jest. Gdy potwierdzisz, że to jest problem, możesz dokładniej przeanalizować dane 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.toSorted((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 pracować z danymi LoAF, aby ustalić dokładną przyczynę interakcji z wysokimi wartościami czasu przetwarzania, w tym:
- Element i jego zarejestrowany detektor zdarzeń.
- Plik skryptu i pozycja znaku w nim, który zawiera kod długo działającego modułu obsługi zdarzeń.
- Nazwa funkcji.
Tego typu dane są bezcenne. Nie musisz już wykonywać żmudnej pracy polegającej na ustalaniu, która interakcja – lub który z jej modułów obsługi zdarzeń – odpowiada za wysokie wartości czasu przetwarzania. Ponadto skrypty innych firm często mogą rejestrować własne moduły obsługi zdarzeń, dzięki czemu możesz określić, czy to Twój kod był odpowiedzialny za dany problem. W przypadku kodu, nad którym masz kontrolę, warto przyjrzeć się optymalizacji długich zadań.
Długie opóźnienia wejściowe
Długo działające moduły obsługi zdarzeń są powszechne, ale należy wziąć pod uwagę inne części interakcji. Jedna część występuje przed czasem przetwarzania i jest nazywana opóźnieniem wejściowym. Jest to czas od momentu, gdy użytkownik zainicjuje interakcję, do momentu, gdy zaczną działać wywołania zwrotne modułu obsługi zdarzeń. Dzieje się tak, gdy wątek główny przetwarza już inne zadanie. Kompilacja atrybucji biblioteki web-vitals może podać długość opóźnienia wejściowego 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ą duże opóźnienia, musisz dowiedzieć się, co działo się na stronie w momencie interakcji, która spowodowała długie opóźnienie. Często sprowadza się to do tego, czy interakcja miała miejsce podczas wczytywania strony, czy po nim.
Czy błąd wystąpił podczas wczytywania strony?
Wątek główny jest często najbardziej obciążony podczas wczytywania strony. W tym czasie w kolejce są ustawiane i przetwarzane różne zadania. Jeśli użytkownik spróbuje wejść w interakcję ze stroną, gdy wszystkie te zadania są wykonywane, może to opóźnić interakcję. Strony, które wczytują dużo kodu JavaScript, mogą rozpocząć kompilowanie i ocenianie skryptów, a także wykonywanie funkcji, które przygotowują stronę do interakcji z użytkownikiem. Może to przeszkadzać, jeśli użytkownik wejdzie w interakcję z witryną w trakcie wykonywania tych zadań. 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.toSorted((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 zarejestrujesz te dane w terenie i zobaczysz duże opóźnienia wejściowe oraz typy wywołujących 'classic-script' lub 'module-script', można stwierdzić, że ocena skryptów w Twojej witrynie trwa długo i blokują one wątek główny na tyle długo, że opóźniają interakcje. Możesz skrócić ten czas blokowania, dzieląc skrypty na mniejsze pakiety, odraczając ładowanie początkowo nieużywanego kodu na później i sprawdzając witrynę pod kątem nieużywanego kodu, który można całkowicie usunąć.
Czy nastąpiło to po wczytaniu strony?
Opóźnienia reakcji często występują podczas wczytywania strony, ale mogą też wystąpić po jej wczytaniu z zupełnie innego powodu. Typowe przyczyny opóźnień wejściowych po wczytaniu strony to kod, który jest uruchamiany okresowo z powodu wcześniejszego wywołania setInterval, lub nawet wywołania zwrotne zdarzeń, które zostały wcześniej dodane do kolejki i nadal są 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.toSorted((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, duże opóźnienia wejściowe spowodowane wspomnianymi wcześniej przyczynami dostarczą szczegółowych danych o atrybucji skryptów. Różnica polega jednak na tym, że typ wywołującego zmienia się w zależności od charakteru pracy, która opóźniła interakcję:
'user-callback'wskazuje, że zadanie blokujące pochodziło zsetInterval,setTimeoutlub nawetrequestAnimationFrame.'event-listener'oznacza, że zadanie blokujące pochodziło z wcześniejszych danych wejściowych, które zostały umieszczone w kolejce i nadal są przetwarzane.'resolve-promise'i'reject-promise'oznaczają, że zadanie blokujące pochodziło z asynchronicznego działania, które zostało uruchomione wcześniej i rozwiązane lub odrzucone w momencie, gdy użytkownik próbował wejść w interakcję ze stroną, co opóźniło interakcję.
W każdym przypadku dane o atrybucji skryptu pozwolą Ci określić, od czego zacząć poszukiwania i czy opóźnienie wejściowe było spowodowane przez Twój własny kod czy przez skrypt innej firmy.
Długie opóźnienia prezentacji
Opóźnienia prezentacji to ostatni etap interakcji, który rozpoczyna się po zakończeniu modułów obsługi zdarzeń interakcji i trwa do momentu wyrenderowania następnej klatki. Występują, gdy praca w module obsługi zdarzeń spowodowana interakcją zmienia stan wizualny interfejsu. Podobnie jak w przypadku czasu przetwarzania i opóźnienia wejściowego, biblioteka web-vitals może podać czas opóźnienia prezentacji w przypadku interakcji:
import {onINP} from 'web-vitals/attribution';
onINP(({name, value, attribution}) => {
const {presentationDelay} = attribution; // 113.32307691
});
Jeśli rejestrujesz te dane i widzisz duże opóźnienia prezentacji w przypadku interakcji, które wpływają na INP witryny, przyczyny mogą być różne, ale oto kilka z nich, na które warto zwrócić uwagę.
Kosztowne prace nad stylem i układem
Długie opóźnienia w wyświetlaniu mogą być spowodowane kosztownym ponownym obliczaniem stylów i układu, które wynika z różnych przyczyn, takich jak złożone selektory CSS i duże rozmiary DOM. Czas trwania tej pracy możesz zmierzyć za pomocą czasów 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.toSorted((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 ramki, ale informuje, kiedy się ona rozpoczęła. Dzięki tej sygnaturze czasowej rozpoczęcia możesz użyć innych danych z LoAF, aby obliczyć dokładny czas trwania tej pracy, określając czas zakończenia ramki i odejmując od niego sygnaturę czasową rozpoczęcia pracy nad stylem i układem.
Długotrwałe wywołania zwrotne requestAnimationFrame
Jedną z potencjalnych przyczyn długich opóźnień prezentacji jest nadmierna ilość pracy wykonywanej w wywołaniu zwrotnym requestAnimationFrame. Zawartość tego wywołania zwrotnego jest wykonywana po zakończeniu działania modułów obsługi zdarzeń, ale tuż przed ponownym obliczeniem stylu i pracą nad układem.
Jeśli wykonywane w nich zadania są złożone, ich realizacja może zająć sporo czasu. Jeśli podejrzewasz, że wysokie wartości opóźnienia prezentacji są spowodowane pracą wykonywaną za pomocą requestAnimationFrame, możesz użyć danych LoAF udostępnianych 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.toSorted((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 spędzana w wywołaniu zwrotnym requestAnimationFrame, upewnij się, że praca wykonywana w tych wywołaniach zwrotnych ogranicza się do działań, które powodują rzeczywistą aktualizację interfejsu. Wszelkie inne działania, które nie wpływają na DOM ani nie aktualizują stylów, niepotrzebnie opóźniają renderowanie następnej klatki, więc zachowaj ostrożność.
Podsumowanie
Dane zgromadzone przez Chrome UX Report to najlepsze źródło informacji, z którego możesz skorzystać, aby dowiedzieć się, które interakcje są problematyczne dla rzeczywistych użytkowników. Korzystając z narzędzi do zbierania danych z pola, takich jak biblioteka JavaScript web-vitals (lub dostawca RUM), możesz z większą pewnością określić, które interakcje są najbardziej problematyczne, a następnie przejść do odtwarzania problematycznych interakcji w laboratorium i ich naprawiania.