Optymalizacja interakcji do kolejnego wyrenderowania

Dowiedz się, jak zoptymalizować czas od interakcji do kolejnego wyrenderowania w witrynie.

Data publikacji: 19 maja 2023 r., ostatnia aktualizacja: 2 września 2025 r.

Interakcja do kolejnego wyrenderowania (INP) to stabilny podstawowy wskaźnik internetowy, który ocenia ogólną responsywność strony na interakcje użytkowników na podstawie obserwacji czasu oczekiwania w odniesieniu do wszystkich kwalifikujących się interakcji w całym okresie odwiedzania strony przez użytkownika. Ostateczna wartość INP to najdłuższa zaobserwowana interakcja (czasami z pominięciem wartości odstających).

Aby zapewniać użytkownikom dobre wrażenia, wartość interakcji do kolejnego wyrenderowania powinna wynosić 200 milisekund lub mniej. Aby osiągnąć ten cel w przypadku większości użytkowników, dobrym progiem do zmierzenia jest 75 percentyl wczytywania stron, podzielony na urządzenia mobilne i komputery.

Dobre wartości INP to 200 milisekund lub mniej, złe wartości to ponad 500 milisekund, a wszystko pomiędzy wymaga poprawy.
Wartości progowe INP

W zależności od witryny może być niewiele interakcji lub może ich nie być wcale – na przykład na stronach zawierających głównie tekst i obrazy z niewielką liczbą elementów interaktywnych lub bez nich. W przypadku witryn takich jak edytory tekstu lub gry może być ich setki, a nawet tysiące. W obu przypadkach, gdy INP jest wysoki, wygoda użytkowników jest zagrożona.

Poprawa INP wymaga czasu i wysiłku, ale nagrodą są lepsze wrażenia użytkowników. W tym przewodniku omówimy sposób poprawy INP.

Ustalanie przyczyny niskiego INP

Zanim zaczniesz rozwiązywać problemy z powolnymi interakcjami, musisz mieć dane, które pozwolą Ci stwierdzić, czy wskaźnik INP Twojej witryny jest słaby lub wymaga poprawy. Gdy masz już te informacje, możesz przejść do laboratorium, aby zacząć diagnozować powolne interakcje i pracować nad rozwiązaniem.

Znajdowanie powolnych interakcji w terenie

Najlepiej zacząć optymalizację INP od danych zgromadzonych przez użytkowników. W najlepszym przypadku dane zgromadzone przez dostawcę usługi monitorowania rzeczywistych użytkowników (RUM) dostarczą nie tylko wartości INP strony, ale też danych kontekstowych, które wskazują, jaka konkretna interakcja odpowiada za wartość INP, czy interakcja miała miejsce podczas wczytywania strony czy po nim, jaki był typ interakcji (kliknięcie, naciśnięcie klawisza czy dotknięcie) oraz inne cenne informacje.

Jeśli nie korzystasz z usług dostawcy RUM, aby uzyskać dane z pól, w przewodniku po danych z pól INP znajdziesz poradę, aby użyć PageSpeed Insights do wyświetlenia danych z Raportu na temat użytkowania Chrome (CrUX), co pomoże Ci uzupełnić luki. CrUX to zbiór danych Google w ramach programu Core Web Vitals, który zawiera ogólne podsumowanie danych dotyczących milionów witryn, w tym INP. CrUX często nie dostarcza jednak danych kontekstowych, które można uzyskać od dostawcy RUM, aby pomóc w analizie problemów. Dlatego zalecamy, aby w miarę możliwości witryny korzystały z usług dostawcy RUM lub wdrażały własne rozwiązanie RUM, które uzupełni dane dostępne w CrUX.

Diagnozowanie powolnych interakcji w laboratorium

Najlepiej zacząć testowanie w laboratorium, gdy masz dane zgromadzone przez użytkowników, które sugerują, że interakcje są powolne. Jeśli nie masz danych zgromadzonych, możesz zastosować kilka strategii identyfikowania powolnych interakcji w laboratorium. Takie strategie obejmują śledzenie typowych wzorców przeglądania i testowanie interakcji po drodze, a także interakcję ze stroną podczas wczytywania, gdy wątek główny jest często najbardziej obciążony, aby zidentyfikować powolne interakcje podczas tej kluczowej części wrażeń użytkownika.

Optymalizacja interakcji

Gdy zidentyfikujesz powolną interakcję i będziesz w stanie odtworzyć ją ręcznie w laboratorium, następnym krokiem będzie jej optymalizacja.

Interakcje można podzielić na 3 podkategorie:

  1. Opóźnienie przy działaniu, które zaczyna się, gdy użytkownik inicjuje interakcję ze stroną, a kończy, gdy zaczynają się wykonywać wywołania zwrotne zdarzenia związane z interakcją.
  2. czas przetwarzania, czyli czas potrzebny na wykonanie wywołań zwrotnych zdarzeń.
  3. opóźnienie prezentacji, czyli czas potrzebny przeglądarce na wyświetlenie następnej klatki, która zawiera wizualny wynik interakcji.
Przykładowa interakcja w głównym wątku. Użytkownik wprowadza dane, gdy są wykonywane zadania blokujące. Dane wejściowe są opóźnione do czasu zakończenia tych zadań, po czym uruchamiane są moduły obsługi zdarzeń pointerup, mouseup i zdarzenia kliknięcia, a następnie rozpoczyna się renderowanie i rysowanie, aż do wyświetlenia następnej klatki.
Cykl życia interakcji. Opóźnienie wejściowe występuje do momentu rozpoczęcia działania modułów obsługi zdarzeń. Może być spowodowane przez czynniki takie jak długie zadania w głównym wątku. Następnie uruchamiane są wywołania zwrotne modułu obsługi zdarzeń interakcji, a przed wyświetleniem następnej klatki występuje opóźnienie.

Suma tych 3 części składowych to całkowity czas oczekiwania na interakcję. Każda część interakcji przyczynia się do całkowitego opóźnienia interakcji, dlatego ważne jest, aby wiedzieć, jak zoptymalizować każdą część interakcji, aby działała jak najkrócej.

Identyfikowanie i zmniejszanie opóźnienia wejściowego

Gdy użytkownik wchodzi w interakcję ze stroną, pierwszą częścią tej interakcji jest opóźnienie przy działaniu. W zależności od innych działań na stronie opóźnienia przy wprowadzaniu danych mogą być znaczne. Może to być spowodowane aktywnością w głównym wątku (np. wczytywaniem, analizowaniem i kompilowaniem skryptów), obsługą pobierania, funkcjami licznika czasu, a nawet innymi interakcjami, które występują w krótkich odstępach czasu i nakładają się na siebie.

Niezależnie od źródła opóźnienia wejściowego interakcji należy je ograniczyć do minimum, aby interakcje mogły jak najszybciej zacząć uruchamiać wywołania zwrotne zdarzeń.

Związek między oceną skryptu a długimi zadaniami podczas uruchamiania

Kluczowym aspektem interaktywności w cyklu życia strony jest uruchamianie. Podczas wczytywania strony jest ona najpierw renderowana, ale należy pamiętać, że to, że strona została wyrenderowana, nie oznacza, że została w pełni wczytana. W zależności od tego, ile zasobów wymaga strona, aby stać się w pełni funkcjonalna, użytkownicy mogą próbować wejść z nią w interakcję, gdy jeszcze się wczytuje.

Jedną z rzeczy, które mogą wydłużyć opóźnienie wejściowe interakcji podczas wczytywania strony, jest ocena skryptu. Po pobraniu pliku JavaScript z sieci przeglądarka musi jeszcze wykonać pewne czynności, zanim będzie mogła go uruchomić. Obejmują one analizę skryptu w celu sprawdzenia, czy jego składnia jest prawidłowa, skompilowanie go do kodu bajtowego, a następnie wykonanie.

W zależności od rozmiaru skryptu może to powodować długie zadania w głównym wątku, co opóźni reakcję przeglądarki na inne interakcje użytkownika. Aby strona reagowała na działania użytkownika podczas wczytywania, musisz wiedzieć, co możesz zrobić, aby zmniejszyć prawdopodobieństwo wystąpienia długich zadań podczas wczytywania strony, dzięki czemu będzie ona działać szybko.

Optymalizacja wywołań zwrotnych zdarzeń

Opóźnienie wejściowe to tylko pierwsza część tego, co mierzy INP. Musisz też zadbać o to, aby wywołania zwrotne zdarzeń, które są uruchamiane w odpowiedzi na interakcję użytkownika, mogły zostać wykonane jak najszybciej.

Często przekazuj sterowanie do wątku głównego

Najlepszą ogólną radą dotyczącą optymalizacji wywołań zwrotnych zdarzeń jest wykonywanie w nich jak najmniejszej liczby działań. Logika interakcji może być jednak złożona i możesz tylko nieznacznie ograniczyć ich pracę.

Jeśli tak jest w przypadku Twojej witryny, możesz spróbować podzielić pracę w wywołaniach zwrotnych zdarzeń na osobne zadania. Dzięki temu zbiorcze zadanie nie stanie się długim zadaniem, które blokuje wątek główny, co pozwala na szybsze uruchamianie innych interakcji, które w przeciwnym razie czekałyby na wątek główny.

setTimeout to jeden ze sposobów na podział zadań, ponieważ przekazywane do niego wywołanie zwrotne jest uruchamiane w nowym zadaniu. Możesz użyć funkcji setTimeout samodzielnie lub wyodrębnić jej użycie do osobnej funkcji, aby uzyskać bardziej ergonomiczne przekazywanie.

Przekazywanie bez rozróżnienia jest lepsze niż brak przekazywania, ale istnieje bardziej szczegółowy sposób przekazywania do wątku głównego, który polega na przekazywaniu tylko bezpośrednio po wywołaniu zwrotnym zdarzenia, które aktualizuje interfejs użytkownika, dzięki czemu logika renderowania może działać szybciej.

Zrezygnuj z wykonywania zadań, aby umożliwić szybsze renderowanie

Bardziej zaawansowana technika przekazywania polega na takim ustrukturyzowaniu kodu w wywołaniach zwrotnych zdarzeń, aby ograniczyć to, co jest uruchamiane, tylko do logiki wymaganej do zastosowania aktualizacji wizualnych w następnej klatce. Wszystko inne można odłożyć na później. Dzięki temu wywołania zwrotne są lekkie i szybkie, a czas renderowania interakcji jest krótszy, ponieważ aktualizacje wizualne nie są blokowane przez kod wywołania zwrotnego zdarzenia.

Wyobraź sobie na przykład edytor tekstu sformatowanego, który formatuje tekst podczas pisania, ale też aktualizuje inne aspekty interfejsu w odpowiedzi na to, co zostało napisane (np. liczbę słów, wyróżnianie błędów pisowni i inne ważne informacje wizualne). Dodatkowo aplikacja może potrzebować zapisać to, co zostało napisane, aby po opuszczeniu i powrocie nie stracić żadnych danych.

W tym przykładzie w odpowiedzi na znaki wpisywane przez użytkownika muszą nastąpić 4 zdarzenia. Jednak tylko pierwszy element musi zostać wykonany przed wyświetleniem następnej klatki.

  1. Zaktualizuj pole tekstowe, wpisując tekst wpisany przez użytkownika, i zastosuj wymagane formatowanie.
  2. Zaktualizuj część interfejsu, która wyświetla bieżącą liczbę słów.
  3. Uruchom logikę, aby sprawdzić błędy pisowni.
  4. Zapisz najnowsze zmiany (lokalnie lub w zdalnej bazie danych).

Kod, który to umożliwia, może wyglądać tak:

textBox.addEventListener('input', (inputEvent) => {
  // Update the UI immediately, so the changes the user made
  // are visible as soon as the next frame is presented.
  updateTextBox(inputEvent);

  // Use `setTimeout` to defer all other work until at least the next
  // frame by queuing a task in a `requestAnimationFrame()` callback.
  requestAnimationFrame(() => {
    setTimeout(() => {
      const text = textBox.textContent;
      updateWordCount(text);
      checkSpelling(text);
      saveChanges(text);
    }, 0);
  });
});

Poniższa wizualizacja pokazuje, jak odroczenie wszelkich niekrytycznych aktualizacji do czasu po następnej klatce może skrócić czas przetwarzania, a tym samym ogólny czas oczekiwania na interakcję.

Ilustracja przedstawiająca interakcję z klawiaturą i kolejne zadania w 2 scenariuszach. Na górnym rysunku zadanie o krytycznym znaczeniu dla renderowania i wszystkie kolejne zadania w tle są wykonywane synchronicznie, dopóki nie pojawi się możliwość wyświetlenia klatki. Na dolnej ilustracji najpierw wykonywane są zadania krytyczne dla renderowania, a następnie przekazywane do wątku głównego, aby szybciej wyświetlić nową klatkę. Zadania w tle są wykonywane później.
Kliknij ilustrację, aby zobaczyć wersję w wysokiej rozdzielczości.

Chociaż użycie setTimeout() w wywołaniu requestAnimationFrame() w poprzednim przykładzie kodu jest nieco ezoteryczne, jest to skuteczna metoda, która działa we wszystkich przeglądarkach i zapobiega blokowaniu następnej klatki przez niekrytyczny kod.

Unikaj niszczenia układu

Przeładowanie układu (czasami nazywane wymuszonym układem synchronicznym) to problem z wydajnością renderowania, który występuje, gdy układ jest renderowany synchronicznie. Dzieje się tak, gdy aktualizujesz style w kodzie JavaScript, a następnie odczytujesz je w tym samym zadaniu. W kodzie JavaScript jest wiele właściwości, które mogą powodować przeciążenie układu.

Wizualizacja niszczenia układu wyświetlana w panelu wydajności w Narzędziach deweloperskich w Chrome.
Przykład niszczenia układu widoczny w panelu wydajności w Narzędziach deweloperskich w Chrome. Zadania renderowania, które powodują przeładowanie układu, będą oznaczone czerwonym trójkątem w prawym górnym rogu części stosu wywołań, często oznaczonej jako Przelicz styl lub Układ.

Przeciążenie układu jest wąskim gardłem wydajności, ponieważ aktualizowanie stylów, a następnie natychmiastowe żądanie wartości tych stylów w kodzie JavaScript zmusza przeglądarkę do wykonywania synchronicznych zadań związanych z układem, które w przeciwnym razie mogłyby zostać wykonane asynchronicznie po zakończeniu wywołań zwrotnych zdarzeń.

Zminimalizuj opóźnienie prezentacji

Opóźnienie prezentacji interakcji obejmuje czas od zakończenia wykonywania wywołań zwrotnych zdarzenia interakcji do momentu, w którym przeglądarka może wyrenderować następną klatkę, która pokazuje wynikające z tego zmiany wizualne.

Zminimalizuj rozmiar DOM

Gdy DOM strony jest mały, renderowanie zwykle kończy się szybko. Jednak gdy drzewa DOM stają się bardzo duże, zadania renderowania zwykle skalują się wraz ze wzrostem rozmiaru DOM. Zależność między renderowaniem a rozmiarem DOM nie jest liniowa, ale duże elementy DOM wymagają więcej pracy przy renderowaniu niż małe. Duży DOM jest problematyczny w 2 przypadkach:

  1. podczas początkowego renderowania strony, gdy duży DOM wymaga dużo pracy, aby wyrenderować początkowy stan strony;
  2. W odpowiedzi na interakcję użytkownika, gdy duży DOM może spowodować, że aktualizacje renderowania będą bardzo kosztowne, a tym samym wydłuży się czas potrzebny przeglądarce na wyświetlenie następnej klatki.

Pamiętaj, że w niektórych przypadkach nie da się znacznie zmniejszyć dużych drzew DOM. Możesz zmniejszyć rozmiar DOM, na przykład przez spłaszczenie DOM lub dodawanie do DOM podczas interakcji użytkownika, aby zachować mały rozmiar początkowy DOM, ale te techniki mogą nie wystarczyć.

Używaj content-visibility do leniwego renderowania elementów poza ekranem

Jednym ze sposobów na ograniczenie ilości pracy związanej z renderowaniem podczas wczytywania strony i w odpowiedzi na interakcje użytkownika jest użycie właściwości CSS content-visibility, która skutecznie opóźnia renderowanie elementów, gdy zbliżają się do widocznego obszaru. Chociaż skuteczne korzystanie z interfejsu content-visibility może wymagać pewnej wprawy, warto sprawdzić, czy jego użycie pozwala skrócić czas renderowania, co może poprawić INP strony.

Pamiętaj o kosztach związanych z wydajnością podczas renderowania kodu HTML za pomocą JavaScriptu

Tam, gdzie jest HTML, występuje też jego analizowanie. Gdy przeglądarka zakończy analizowanie kodu HTML i przekształcanie go w DOM, musi zastosować do niego style, wykonać obliczenia układu, a następnie wyrenderować ten układ. Jest to nieunikniony koszt, ale sposób renderowania kodu HTML ma znaczenie.

Gdy serwer wysyła kod HTML, dociera on do przeglądarki jako strumień. Strumieniowanie oznacza, że odpowiedź HTML z serwera dociera we fragmentach. Przeglądarka optymalizuje sposób obsługi strumienia, analizując przyrostowo fragmenty strumienia w miarę ich docierania i renderując je po kolei. Jest to optymalizacja wydajności, ponieważ przeglądarka domyślnie i automatycznie przekazuje kontrolę podczas wczytywania strony, co nie wymaga żadnych działań z Twojej strony.

Podczas pierwszej wizyty w dowolnej witrynie zawsze pobierana jest pewna ilość kodu HTML. Typowe podejście polega na tym, że na początku pobierana jest minimalna ilość kodu HTML, a następnie do wypełnienia obszaru treści używany jest JavaScript. Kolejne aktualizacje tego obszaru treści również następują w wyniku interakcji użytkownika. Jest to tzw. model aplikacji jednostronicowej. Wadą tego wzorca jest to, że renderowanie kodu HTML za pomocą JavaScriptu po stronie klienta nie tylko wiąże się z kosztem przetwarzania JavaScriptu w celu utworzenia tego kodu HTML, ale też przeglądarka nie zwróci wyniku, dopóki nie zakończy analizowania i renderowania tego kodu HTML.

Warto jednak pamiętać, że nawet witryny, które nie są aplikacjami jednostronicowymi, prawdopodobnie będą wymagały renderowania kodu HTML za pomocą JavaScriptu w wyniku interakcji. Zwykle nie jest to problem, o ile nie renderujesz dużych ilości kodu HTML po stronie klienta, co może opóźnić wyświetlanie następnej klatki. Ważne jest jednak, aby zrozumieć, jak takie podejście do renderowania kodu HTML w przeglądarce wpływa na wydajność i jak może wpłynąć na szybkość reagowania witryny na działania użytkownika, jeśli renderujesz dużo kodu HTML za pomocą JavaScriptu.

Podsumowanie

Poprawianie INP witryny to proces iteracyjny. Gdy naprawisz powolną interakcję w terenie, istnieje duże prawdopodobieństwo, że – zwłaszcza jeśli Twoja witryna zapewnia wiele interaktywności – zaczniesz znajdować inne powolne interakcje i będziesz musiał je również zoptymalizować.

Kluczem do poprawy INP jest wytrwałość. Z czasem możesz poprawić responsywność strony do poziomu, który będzie zadowalać użytkowników. Istnieje też duże prawdopodobieństwo, że podczas opracowywania nowych funkcji dla użytkowników konieczne będzie przeprowadzenie tego samego procesu optymalizacji interakcji, które są dla nich specyficzne. Będzie to wymagało czasu i wysiłku, ale warto je poświęcić.