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 korzystania z CrUX, aby ocenić INP swojej witryny
Jeśli nie zbierasz danych zgromadzonych od użytkowników witryny, możesz zacząć od raportu CrUX. Raport ten zbiera dane z pól od rzeczywistych użytkowników Chrome, którzy wyrazili zgodę na wysyłanie danych telemetrycznych.
Dane CrUX są wyświetlane w różnych obszarach w zależności 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”. Dane z bazy danych CrUX możesz pobierać w Search Console w przypadku unikalnych typów stron.
Na początek możesz wpisać adres URL swojej witryny w PageSpeed Insights. Po wpisaniu adresu URL dane z tego pola (jeśli są dostępne) będą wyświetlane w przypadku wielu wskaźników, m.in. INP. Możesz też użyć przełączników, aby sprawdzić wartości INP dla wymiarów urządzeń mobilnych i komputerów.
Te dane są przydatne, ponieważ informują, czy masz problem. Nie może jednak powiedzieć, co powoduje problemy. 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.
Zbieranie danych polowych za pomocą biblioteki JavaScript web-vitals
web-vitals
Biblioteka JavaScriptu to skrypt, który możesz załadować w witrynie, aby zbierać dane pól od użytkowników witryny. Możesz go używać do rejestrowania różnych danych, w tym INP w przeglądarkach, które je obsługują.
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 w tym miejscu do akcji wkracza wersja atrybucji biblioteki web-vitals.
Wykorzystanie biblioteki web-vitals do tworzenia atrybucji
Budowanie atrybucji w bibliotece web-vitals pozwala wyświetlać dodatkowe dane, które możesz uzyskać od użytkowników, aby lepiej rozwiązywać problemy z interaktywnością, 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
});
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, takie jak:
- „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żą ilość renderowania, która opóźniła wyświetlenie następnej klatki?”
W tabeli poniżej znajdziesz podstawowe dane atrybucji, które możesz uzyskać z biblioteki. Mogą one pomóc Ci określić ogólne przyczyny powolnego działania Twojej witryny:
Klucz obiektu attribution
|
Dane |
---|---|
interactionTarget
|
Selektor 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. |
Począwszy od wersji 4 biblioteki web-vitals możesz uzyskać jeszcze bardziej szczegółowe informacje o problematycznych interakcjach dzięki danym z podziałem na fazy INP (opóźnienie wejścia, czas przetwarzania i opóźnienie wyświetlania) oraz interfejsowi Long Animation Frames API (LoAF).
Long Animation Frames API (LoAF)
Debugowanie interakcji za pomocą danych z pola jest trudnym zadaniem. Dzięki danym z LoAF możesz teraz uzyskać lepsze informacje o przyczynach powolnych interakcji, ponieważ LoAF udostępnia szczegółowe informacje o czasie trwania i inne dane, które możesz wykorzystać do dokładnego określenia przyczyn, a co ważniejsze, źródła problemu w kodzie Twojej 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 w LoAF:
Klucz obiektu wpisu LoAF | Dane |
---|---|
duration
|
Czas trwania długiej klatki animacji do momentu zakończenia układu, z wyłączeniem malowania i kompozytowania. |
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 dodane do kolejki w ramach ramki. Przydatne do określenia opóźnienia wejścia na początku interakcji. |
startTime
|
Sygnatura czasowa rozpoczęcia klatki. |
renderStart
|
Kiedy rozpoczęto renderowanie danej 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ąca informacje o przypisaniu skryptu, które przyczyniają się do INP strony. |
Wszystkie te informacje mogą wiele powiedzieć o tym, co powoduje spowolnienie interakcji, ale szczególne znaczenie ma tablica scripts
, którą wyświetlają wpisy 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 wywołań mogą być wartości takie jak 'IMG#id.onload' , 'Window.requestAnimationFrame' lub '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
Aby pokazać Ci, jak możesz wykorzystywać te informacje, pokażemy Ci w tym przewodniku, jak używać danych LoAF wyświetlanych w bibliotece web-vitals
, aby określić przyczyny powolnego działania 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 obsługującym zdarzenie komponencie, 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.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. Skrypty innych firm często mogą rejestrować własne moduły obsługi zdarzeń, więc 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.
Długie opóźnienia w reakcji na działanie
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 momentu, gdy użytkownik rozpoczyna interakcję, do momentu, gdy zaczynają się wywołania zwrotne jego funkcji obsługi zdarzeń. Występuje on, gdy główny wątek 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ługie opóźnienie w wejściu. 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?
Opóźnienia w rejestrowaniu danych często występują podczas wczytywania strony, ale 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'
wskazuje, że zadanie blokowania pochodziło zsetInterval
,setTimeout
lub nawetrequestAnimationFrame
.'event-listener'
oznacza, że zadanie blokujące pochodzi z wcześniejszego wejścia, które zostało umieszczone w kole i nadal jest przetwarzane.'resolve-promise'
i'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, w którym zostanie narysowany następny kadr. 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 zwiększenia INP witryny, przyczyny mogą być różne, ale warto zwrócić uwagę na kilka z nich.
kosztowne prace związane ze stylem i układem;
Długie opóźnienia w prezentacji mogą być spowodowane kosztownym przeliczaniem stylów i układem, które wynikają z różnych przyczyn, w tym złożonych selektorów CSS i dużych rozmiarów DOM. Czas trwania tego procesu możesz mierzyć za pomocą funkcji pomiaru czasu LoAF dostępnej 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 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 requestAnimationFrame
wywołania zwrotne
Jedną z potencjalnych przyczyn długich opóźnień w prezentacji może być nadmierna ilość pracy wykonanej 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ą skomplikowane, wywołania zwrotne 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 udostępnionych 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 wykorzystywana na wywołania zwrotne requestAnimationFrame
, upewnij się, że podczas tych wywołań wykonywane są tylko czynności, które prowadzą do faktycznej aktualizacji 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 polowych, takich jak biblioteka JavaScripta web-vitals (lub dostawca RUM), możesz dokładniej określić, które interakcje są najbardziej problematyczne, a potem odtworzyć je w laboratorium i je naprawić.
Baner powitalny z Unsplash: Federico Respini.