Optymalizacja interakcji do kolejnego wyrenderowania

Dowiedz się, jak zoptymalizować interakcję z witryną przed następnym wyrenderowaniem.

Interakcja przed kolejnym wyrenderowaniem (INP) to stabilne podstawowe wskaźniki internetowe, które oceniają ogólną responsywność strony na interakcje użytkowników przez obserwację czasu oczekiwania wszystkich kwalifikujących się interakcji, które występują w całym cyklu życia wizyty użytkownika na stronie. Ostateczna wartość INP to najdłuższa zaobserwowana interakcja (czasami z pomijaniem wartości odstających).

Aby zapewnić wygodę użytkownikom, czas interakcji do kolejnego wyrenderowania powinien wynosić maksymalnie 200 milisekund. Aby mieć pewność, że w przypadku większości użytkowników ten cel zostanie osiągnięty, dobrym prógem pomiaru jest 75 centyl wczytywania stron z podziałem na urządzenia mobilne i komputery.

Dobre wartości INP trwają maksymalnie 200 milisekund, słabe wartości dłuższe niż 500 milisekund, a niektóre z nich wymagają poprawy.

W zależności od witryny może nastąpić niewiele interakcji lub nie będzie ich wcale – np. strony zawierające głównie tekst i obrazy bez interaktywnych elementów lub z niewielką liczbą elementów. W przypadku witryn takich jak edytory tekstu czy gry wiążą się z setkami, a nawet tysiącami interakcji. W obu przypadkach, gdy wskaźnik INP jest wysoki, może to negatywnie wpłynąć na wrażenia użytkowników.

Ulepszenie INP wymaga czasu i wysiłku, ale nagroda zapewnia lepsze wrażenia użytkowników. W tym przewodniku omówimy sposoby na poprawę wartości INP.

Dowiedz się, co powoduje niski wskaźnik INP

Aby naprawić problem z powolnymi interakcjami, musisz uzyskać dane, które pozwolą Ci określić, czy wskaźnik INP Twojej witryny jest słaby lub wymaga poprawy. Po uzyskaniu tych informacji możesz przejść do modułu, aby zacząć diagnozować powolne interakcje i znaleźć rozwiązanie.

Znajdź wolne interakcje w terenie

Najlepiej rozpocząć drogę do optymalizacji wartości INP od danych terenowych. Dane pól od dostawcy monitorowania użytkowników rzeczywistego (RUM) dostarczają nie tylko wartości INP strony, ale też dane kontekstowe, które wskazują, która konkretnie interakcja była odpowiedzialna za samą wartość INP, czy miała miejsce podczas wczytywania strony czy po jej wczytaniu, jaki był jej typ (kliknięcie, naciśnięcie klawisza lub dotknięcie) oraz inne cenne informacje.

Jeśli nie polegasz na dostawcy RUM, aby uzyskać dane z terenu, w przewodniku po danych pól INP zalecamy korzystanie z raportu na temat użytkowania Chrome (CrUX) dostępnego w PageSpeed Insights, który pomoże Ci uzupełnić luki. CrUX to oficjalny zbiór danych programu Core Web Vitals, który zawiera ogólne podsumowanie danych dotyczących milionów witryn, w tym INP. Jednak raport na temat użytkowania Chrome często nie udostępnia danych kontekstowych, które pomogłyby Ci w analizowaniu problemów, a które pochodzą od dostawcy RUM. Z tego względu zalecamy, aby w miarę możliwości witryny korzystały z dostawcy RUM lub wdrażały własne rozwiązanie RUM, aby uzupełnić dane dostępne w CrUX.

Diagnozowanie powolnych interakcji w module

Testowanie najlepiej zacząć w laboratorium, gdy masz już dane sugerujące powolne interakcje. W przypadku braku danych można zastosować kilka strategii wykrywania powolnych interakcji w laboratorium. Strategie te obejmują śledzenie typowych schematów działań użytkowników i testowanie interakcji w trakcie ich trwania, a także wchodzenie w interakcje ze stroną podczas wczytywania, gdy wątek główny jest często najbardziej przeciążony, w celu wykrywania wolniejszych interakcji w kluczowym momencie.

Optymalizuj interakcje

Gdy zidentyfikujesz powolną interakcję i będziesz w stanie ręcznie odtworzyć ją w module, następnym krokiem jest jej zoptymalizowanie. Interakcje można podzielić na 3 fazy:

  1. Opóźnienie danych wejściowych, które rozpoczyna się, gdy użytkownik zainicjuje interakcję ze stroną, i kończy się wraz z wywołaniem interakcji zwrotnej zdarzenia.
  2. Czas przetwarzania, czyli czas potrzebny na wykonanie wywołań zwrotnych zdarzeń.
  3. Opóźnienie prezentacji, czyli czas potrzebny przeglądarce na zaprezentowanie następnej klatki, która zawiera wizualny rezultat interakcji.

Suma tych 3 faz to całkowity czas oczekiwania na interakcję. Każdy etap interakcji przekłada się na całkowite opóźnienie interakcji, dlatego musisz wiedzieć, jak ją zoptymalizować, aby trwała jak najkrócej.

Identyfikowanie i zmniejszanie opóźnienia danych wejściowych

Gdy użytkownik wchodzi w interakcję ze stroną, pierwszą częścią tej interakcji jest opóźnienie danych wejściowych. W zależności od innych działań na stronie opóźnienia mogą być znaczne. Może to być spowodowane działaniami występującymi w wątku głównym (np. ze względu na wczytywanie, analizowanie i kompilację), obsługę pobierania, funkcje licznika czasu, a nawet inne interakcje, które zachodzą w krótkich odstępach czasu i się nakładają.

Niezależnie od źródła opóźnienia danych wejściowych w interakcji warto zminimalizować opóźnienie danych wejściowych, aby interakcje mogły jak najszybciej rozpocząć wykonywanie wywołań zwrotnych zdarzeń.

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

Kluczowym aspektem interaktywności w cyklu życia strony jest jej uruchamianie. Po załadowaniu strony jest ona początkowo wyrenderowana. Pamiętaj jednak, że wyrenderowanie strony nie oznacza, że zostanie ona wczytana. W zależności od ilości zasobów potrzebnych do pełnego działania strony może się zdarzyć, że użytkownicy będą próbować z niej korzystać w trakcie jej wczytywania.

Jedną z rzeczy, która może wydłużyć opóźnienie interakcji podczas wczytywania strony, jest ocena skryptu. Gdy plik JavaScript zostanie pobrany z sieci, przeglądarka musi jeszcze wykonać pewne czynności, zanim nastąpi uruchomienie JavaScriptu. Obejmuje to analizę skryptu w celu sprawdzenia, czy jego składnia jest prawidłowa, skompilowanie go do kodu bajtowego i zakończenie.

W zależności od rozmiaru skryptu może to prowadzić do długich zadań w wątku głównym, co opóźni przeglądarkę w reagowaniu na inne interakcje użytkowników. Aby Twoja strona reagowała na działania użytkowników podczas jej wczytywania, musisz wiedzieć, co możesz zrobić, aby zmniejszyć prawdopodobieństwo wykonywania długich zadań podczas wczytywania strony i zachować jej płynność.

Optymalizuj wywołania zwrotne zdarzeń

Opóźnienie danych wejściowych to tylko pierwsza część pomiaru INP. Musisz też dopilnować, by wywołania zwrotne zdarzeń uruchamiane w odpowiedzi na interakcję użytkownika były jak najszybciej realizowane.

często uczestniczy w głównym wątku.

Ogólną radą przy optymalizacji wywołań zwrotnych zdarzeń jest wykonanie w nich jak najmniejszej pracy. Jednak logika interakcji może być skomplikowana i możesz być w stanie tylko nieznacznie ograniczyć nakład pracy.

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 praca zbiorcza nie stanie się długim zadaniem, które zablokuje wątek główny, co umożliwi szybsze rozpoczęcie innych interakcji, które w innym przypadku czekałyby na wątek główny.

setTimeout to jeden ze sposobów podziału zadań, ponieważ przekazane do niego wywołanie zwrotne jest uruchamiane w nowym zadaniu. Możesz używać samej funkcji setTimeout lub wyodrębnić ją w osobnej funkcji, aby uzyskać bardziej ergonomiczne rozwiązanie.

Lepszy jest niedyskryminowany rezygnacja niż rezygnacja z uzyskiwania zwrotu. Istnieje jednak bardziej zróżnicowany sposób poddania się wątku głównym i wymagający reagowania natychmiast po wywołaniu zdarzenia, które aktualizuje interfejs użytkownika, tak aby logika renderowania mogła działać wcześniej.

Pozwól na szybsze rozpoczęcie pracy z renderowaniem

Bardziej zaawansowana technika generowania zysków obejmuje opracowanie struktury kodu w wywołaniach zwrotnych zdarzeń w celu ograniczenia przebiegu procesu do logiki wymaganej do zastosowania aktualizacji wizualnych w następnej klatce. Resztę można odłożyć do innego zadania. Dzięki temu wywołania zwrotne nie tylko są szybkie i sprawne, lecz także skracają czas renderowania interakcji, ponieważ nie pozwalają na blokowanie aktualizacji wizualnych w kodzie wywołania zwrotnego zdarzenia.

Wyobraź sobie na przykład edytor tekstu sformatowanego, który formatuje tekst podczas pisania i aktualizuje inne aspekty interfejsu w zależności od tego, co piszesz (np. liczba słów, wyróżnianie błędów pisowni i inne ważne informacje wizualne). Ponadto aplikacja może być zmuszona do zapisania wprowadzonej treści, aby w przypadku jej zamknięcia i powrotu nie utracić żadnej pracy.

W tym przykładzie w odpowiedzi na znaki wpisywane przez użytkownika muszą wystąpić cztery następujące rzeczy: Jednak zanim pojawi się następna klatka, wystarczy zrobić pierwszy element.

  1. Wpisz w polu tekstowym tekst wpisany przez użytkownika i zastosuj wymagane formatowanie.
  2. Zaktualizuj część interfejsu, w której wyświetla się bieżąca liczba słów.
  3. Uruchom funkcję logiczną, aby sprawdzić pisownię.
  4. Zapisz najnowsze zmiany (lokalnie lub w zdalnej bazie danych).

Kod, który to umożliwia, może wyglądać mniej więcej 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);
  });
});

Ta wizualizacja pokazuje, jak odroczenie niekrytycznych aktualizacji do następnej klatki może skrócić czas przetwarzania, a tym samym ogólny czas oczekiwania na interakcję.

Ilustracja przedstawiająca interakcję z klawiaturą i późniejsze zadania w 2 scenariuszach. Na górze ilustracji zadanie kluczowe związane z renderowaniem i wszystkie kolejne zadania w tle są uruchamiane synchronicznie, aż pojawi się możliwość zaprezentowania klatki. W dolnej części grafika o znaczeniu krytycznym podczas renderowania jest najpierw uruchamiana, a następnie przechodzi do wątku głównego, aby szybciej przedstawić nową klatkę. Później będą uruchamiane zadania w tle.
Kliknij ten rysunek, aby zobaczyć wersję w wysokiej rozdzielczości.

Chociaż użycie funkcji setTimeout() w wywołaniu requestAnimationFrame() w poprzednim przykładzie kodu można uznać za nieco ezoteryczne, jest to skuteczna metoda działająca we wszystkich przeglądarkach, aby mieć pewność, że niekrytyczny kod nie blokuje następnej klatki.

Unikanie thrashingu układu

Trafikowanie układów (czasami nazywane wymuszonym układem synchronicznym) to problem z wydajnością renderowania, w którym układ występuje synchronicznie. Dzieje się tak, gdy zaktualizujesz style w JavaScripcie, a następnie odczytasz je w tym samym zadaniu. Istnieje wiele właściwości w JavaScripcie, które mogą powodować thrashing układu.

Wizualizacja miksowania układu w panelu wydajności w Narzędziach deweloperskich w Chrome.
Przykład thrashingu układu widoczny w panelu wydajności Narzędzi deweloperskich w Chrome. Zadania renderowania, które obejmują niszczenie układu, są oznaczone czerwonym trójkątem w prawym górnym rogu stosu wywołań, często oznaczonymi jako Ponownie oblicz styl lub Układ.

Niskie zużycie układu to wąskie gardło wydajności, ponieważ po zaktualizowaniu stylów i natychmiastowym wysyłaniu żądań wartości tych stylów w języku JavaScript przeglądarka jest zmuszona przeprowadzić prace nad układem synchronicznym, w przeciwnym razie musiałaby poczekać asynchronicznie po zakończeniu realizacji wywołań zwrotnych zdarzeń.

Minimalizuj opóźnienie prezentacji

Opóźnienie prezentacji interakcji oznacza okres od momentu zakończenia wywołań zwrotnych zdarzenia interakcji do momentu, w którym przeglądarka jest w stanie wyrenderować następną klatkę, która pokazuje wynikające z niego zmiany wizualne.

Minimalizuj rozmiar DOM

Jeśli DOM strony jest mały, renderowanie zwykle szybko się kończy. Jednak gdy modele DOM stają się bardzo duże, zadania renderowania na ogół skalują się wraz ze wzrostem rozmiaru DOM. Związek między renderowaniem a rozmiarem DOM nie jest liniowy, ale duże DOM wymagają więcej pracy niż małe DOM. Duży DOM stanowi problem w 2 przypadkach:

  1. Podczas wstępnego renderowania strony, gdy duży DOM wymaga dużej pracy, by wyrenderować początkowy stan strony.
  2. W odpowiedzi na interakcję użytkownika – duży DOM może spowodować, że aktualizacje renderowania są bardzo kosztowne i wydłużają czas potrzebny przeglądarce na wyświetlenie następnej klatki.

Pamiętaj, że w niektórych przypadkach duże DOM nie można znacznie ograniczyć. Istnieją sposoby na zmniejszenie rozmiaru DOM, takie jak spłaszczenie DOM lub dodanie go podczas interakcji z użytkownikiem, aby zmniejszyć początkowy rozmiar DOM, jednak rozwiązania te mogą okazać się nieskuteczne.

Aby leniwie renderować elementy poza ekranem, użyj komponentu content-visibility

Jednym ze sposobów ograniczenia czasochłonnego renderowania zarówno podczas wczytywania strony, jak i renderowania w odpowiedzi na interakcje użytkowników jest wykorzystanie właściwości CSS content-visibility, która w efekcie oznacza leniwe renderowanie elementów zbliżających się do widocznego obszaru. Korzystanie z interfejsu content-visibility może trochę potrwać, ale warto sprawdzić, czy skrócenie czasu renderowania może poprawić wartość INP strony.

Pamiętaj o kosztach związanych z wydajnością podczas renderowania kodu HTML za pomocą języka JavaScript.

Tam, gdzie jest kod HTML, jest też analiza HTML. Gdy tylko zakończy ona analizę kodu HTML w modelu DOM, musi zastosować do niego style, wykonać obliczenia układu, a następnie wyrenderować układ. Jest to nieunikniony koszt, ale ma znaczenie sposób renderowania kodu HTML.

Gdy serwer wysyła kod HTML, pojawia się on w przeglądarce w postaci strumienia. Strumieniowanie oznacza, że odpowiedź HTML z serwera napływa we fragmentach. Przeglądarka optymalizuje sposób obsługi strumienia, stopniowo analizując jego fragmenty w miarę ich napływania i renderując je kawałek po bitwie. Jest to optymalizacja wydajności, ponieważ przeglądarka domyślnie generuje przychody okresowo i automatycznie podczas wczytywania strony, a do tego bezpłatnie.

Choć pierwsze wejście na którąś z witryn wymaga zawsze pewnej ilości kodu HTML, typowym podejściem jest zwykle użycie niewielkiego fragmentu kodu HTML, a dopiero później wypełnianie obszaru treści za pomocą JavaScriptu. Kolejne aktualizacje tego obszaru treści również są wynikiem interakcji użytkowników. Nazywa się to zwykle modelem aplikacji jednostronicowej (SPA). Wadą tego wzorca jest to, że renderowanie kodu HTML za pomocą JavaScriptu na kliencie wiąże się nie tylko z kosztami przetwarzania JavaScriptu w celu jego utworzenia, ale także, że przeglądarka nie zacznie działać, dopóki nie zakończy analizy kodu HTML i nie wyrenderuje go.

Pamiętaj jednak, że nawet witryny, które nie są aplikacjami SPA, w wyniku interakcji będą prawdopodobnie wymagać renderowania HTML przez JavaScript. Jest to zwykle normalne, o ile nie renderujesz na kliencie dużych ilości kodu HTML, ponieważ może to opóźnić prezentację następnej klatki. Warto jednak poznać wpływ tego podejścia na wydajność renderowania kodu HTML w przeglądarce i jego wpływ na responsywność witryny na dane wejściowe użytkownika, jeśli renderujesz dużo kodu HTML za pomocą JavaScriptu.

Podsumowanie

Zwiększanie wskaźnika INP witryny jest procesem iteracyjnym. Jeśli naprawisz powolne interakcje w terenie, jest duża szansa, że – szczególnie jeśli witryna zawiera dużo interaktywności – zauważysz inne powolne interakcje i konieczne będzie ich zoptymalizowanie.

Kluczem do poprawy INP jest trwałość. Możesz z czasem dostosować swoją responsywność do miejsca, w którym użytkownicy będą zadowoleni z udostępnionych przez Ciebie treści. Możliwe jest również, że w miarę opracowywania nowych funkcji dla użytkowników konieczne może być przejście tego samego procesu w zakresie optymalizacji interakcji pod ich kątem. Wymaga to czasu i wysiłku, ale jest to świetna praca i czas, który trzeba poświęcić.

Baner powitalny z filmu Unsplash autorstwa Davida Pisnoya i zmodyfikowany zgodnie z licencją Unsplash.