Optymalizacja największego wyrenderowania treści

Szczegółowy przewodnik, z którego dowiesz się, jak podzielić LCP i wskazać kluczowe obszary wymagające poprawy.

Największe wyrenderowanie treści (LCP) to jeden z 3 podstawowych wskaźników internetowych i wskazuje, jak szybko wczytuje się główna zawartość strony internetowej. Dokładniej mówiąc, LCP mierzy czas od rozpoczęcia wczytywania strony przez użytkownika do wyrenderowania największego obrazu lub bloku tekstowego w widocznym obszarze.

W trosce o wygodę użytkowników wskaźnik LCP witryny powinien wynosić nie więcej niż 2, 5 sekundy w przypadku co najmniej 75% wizyt.

Dobre wartości LCP wynoszą maksymalnie 2,5 sekundy, niskie wartości powyżej 4,0 sekund, a niektóre z nich wymagają poprawy
Dobra wartość LCP wynosi maksymalnie 2,5 sekundy.

Na szybkość wczytywania i renderowania strony internetowej przez przeglądarkę może mieć wpływ wiele czynników, a opóźnienia w każdym z nich mogą mieć znaczący wpływ na LCP.

Rzadko się zdarza, by szybkie rozwiązanie problemu w jednym fragmencie strony przynosiło znaczącą poprawę wskaźnika LCP. Aby poprawić LCP, trzeba przyjrzeć się całemu procesowi wczytywania i upewnić się, że każdy jego etap jest zoptymalizowany.

Jak interpretować wskaźnik LCP

Przed zoptymalizowaniem LCP deweloperzy powinni się dowiedzieć, czy mają w ogóle problemy z LCP i jaki jest ich zakres.

LCP można mierzyć za pomocą różnych narzędzi, ale nie wszystkie z nich mierzą LCP w taki sam sposób. Aby zrozumieć LCP prawdziwych użytkowników, spójrz na to, co faktycznie dzieje się u prawdziwych użytkowników, a nie o to, co pokazuje narzędzie laboratoryjne, np. Lighthouse, czy lokalne testy. Te laboratoryjne narzędzia mogą dostarczyć mnóstwo informacji, które pomogą Ci zrozumieć i poprawić LCP. Pamiętaj jednak, że same testy laboratoryjne mogą nie być całkowicie reprezentatywne dla rzeczywistych użytkowników.

Dane LCP na podstawie rzeczywistych użytkowników można uzyskać z narzędzi do monitorowania użytkowników (RUM) zainstalowanych w witrynie lub z raportu na temat użytkowania Chrome (CrUX), który zbiera anonimowe dane od prawdziwych użytkowników Chrome z milionów witryn.

Korzystanie z danych LCP narzędzia PageSpeed Insights CrUX

Narzędzie PageSpeed Insights umożliwia dostęp do danych raportu CrUX w górnej sekcji oznaczonej etykietą Dowiedz się, co robią użytkownicy. Bardziej szczegółowe dane laboratoryjne znajdziesz w dolnej sekcji Diagnozuj problemy z wydajnością. Jeśli dane raportu na temat użytkowania Chrome są dostępne w przypadku Twojej witryny, w pierwszej kolejności skoncentruj się na prawdziwych danych użytkownika.

Dane na temat użytkowania Chrome wyświetlane w PageSpeed Insights
Dane na temat użytkowania Chrome wyświetlane w PageSpeed Insights.
.

PageSpeed Insights może wyświetlać maksymalnie cztery różne dane raportu CrUX:

  • Mobilna transmisja danych dla tego adresu URL
  • Dane komputerów dla tego adresu URL
  • Mobilna transmisja danych dla całego obszaru Origin
  • Dane dla komputerów dla całego obszaru Origin

Możesz to zrobić za pomocą elementów sterujących w prawym górnym i prawym górnym rogu tej sekcji. Jeśli URL nie ma wystarczającej ilości danych do wyświetlenia na poziomie adresu URL, ale ma dane na temat źródła, PageSpeed Insights zawsze pokazuje dane o pochodzeniu.

Statystyki PageSpeed wracają do danych na poziomie źródła, gdy dane na poziomie adresu URL są niedostępne
Jeśli PageSpeed Insights nie ma danych na poziomie adresu URL, pokazuje danych na poziomie punktu początkowego.

Wartość LCP całego punktu początkowego może się znacznie różnić od LCP pojedynczej strony w zależności od tego, jak LCP jest wczytywany na tej stronie w porównaniu z innymi stronami w tej witrynie. To również sposób, w jaki użytkownicy przechodzą do tych stron. Strony główne są zazwyczaj odwiedzane przez nowych użytkowników i dlatego mogą być ładowane „zimno” i bez zawartości pamięci podręcznej. To samo dotyczy najwolniejszych stron w witrynie.

Omówienie 4 różnych kategorii danych raportu na temat użytkowania Chrome może pomóc Ci zrozumieć, czy problem LCP dotyczy tylko tej strony, czy może problemu ogólnego z całą witryną. Może też pokazać, na jakich typach urządzeń występują problemy z LCP.

Korzystanie z dodatkowych danych CrUX na potrzeby PageSpeed Insights

Osoby, które chcą zoptymalizować LCP, powinny też korzystać z czasów Pierwsze wyrenderowanie treści (FCP) i Czas do pierwszego bajtu (TTFB). Są to dobre dane diagnostyczne, które dostarczają cennych informacji o LCP.

TTFB to czas, od którego użytkownik zaczyna przechodzić na stronę (np. klika link) do momentu otrzymania pierwszych bajtów dokumentu HTML. Wysoki wskaźnik TTFB może sprawić, że uzyskanie 2,5-sekundowego LCP będzie trudne, a nawet niemożliwe.

Duża wartość TTFB może być spowodowana przez wiele przekierowań do serwerów, użytkowników, którzy znajdują się daleko od serwera witryny, użytkownicy, którzy korzystają ze słabych warunków sieciowych, lub niemożność wykorzystania treści z pamięci podręcznej ze względu na parametry zapytania.

Po rozpoczęciu renderowania strony może być początkowo wyrenderowane (np. kolor tła), a potem pojawi się jakaś treść (np. nagłówek witryny). Wygląd początkowych treści jest mierzony za pomocą wskaźnika FCP. Różnica między FCP a innymi wskaźnikami może być bardzo znacząca.

Duża różnica między TTFB a FCP może oznaczać, że przeglądarka musi pobrać dużo zasobów blokujących renderowanie. Może to również oznaczać, że musi włożyć wiele pracy, aby wyrenderować wartościowe treści. Jest to klasyczny oznaka witryny, w której w dużym stopniu opiera się na renderowaniu po stronie klienta.

Duża różnica między FCP a LCP oznacza, że zasób LCP nie jest od razu dostępny dla przeglądarki, aby ustalić priorytety (np. tekst lub obrazy zarządzane przez JavaScript, a nie dostępne w początkowym kodzie HTML) albo że przeglądarka wykonuje coś innego, zanim wyświetli treść LCP.

Korzystanie z danych PageSpeed Insights Lighthouse

W sekcji dotyczącej narzędzia Lighthouse na PageSpeed Insights znajdziesz wskazówki dotyczące poprawy LCP. Najpierw sprawdź jednak, czy podany wskaźnik LCP jest w ogóle zgodny z prawdziwymi danymi użytkowników dostarczanymi przez CrUX. Jeśli narzędzia Lighthouse i CrUX się nie zgadzają, prawdopodobnie CrUX zapewnia dokładniejszy obraz wrażeń użytkownika. Zanim podejmiesz działanie, upewnij się, że dane raportu na temat użytkowania Chrome dotyczą Twojej strony, a nie jej pełnego pochodzenia.

Jeśli zarówno w LCP, jak i w raporcie CrUX wyświetlają się wartości LCP, które wymagają poprawy, w sekcji dotyczącej narzędzia Lighthouse znajdziesz cenne wskazówki, jak poprawić LCP. Użyj filtra LCP, aby wyświetlić tylko te kontrole dotyczące LCP:

Możliwości i diagnostyka LCP w LCP
Diagnostyka Lighthouse i sugestie dotyczące poprawy LCP.

Oprócz możliwości ulepszenia znajdziesz też informacje diagnostyczne, które pomogą Ci zdiagnozować problem. Diagnostyka elementu największego wyrenderowania treści pokazuje przydatne zestawienie różnych momentów składających się na LCP:

Etapy LCP w LCP
Podział elementów LCP w narzędziu Lighthouse.

Dalej omówimy te podczęści.

Zestawienie LCP

Optymalizacja LCP może być bardziej skomplikowanym zadaniem, jeśli narzędzie PageSpeed Insights nie dostarcza właściwych sposobów na poprawę tych danych. W przypadku złożonych zadań lepiej jest podzielić je na mniejsze, łatwiejsze do wykonania zadania i rozwiązać je z osobna.

W tej sekcji przedstawiamy metodologię podziału LCP na najważniejsze części, a potem przedstawianie konkretnych zaleceń i sprawdzonych metod optymalizacji każdej z nich.

Większość ładowań stron obejmuje zwykle pewną liczbę żądań sieciowych, ale dla identyfikacji możliwości poprawy LCP należy zacząć od przeanalizowania tylko 2 z nich:

  1. Początkowy dokument HTML
  2. Zasób LCP (jeśli jest potrzebny).

Chociaż inne żądania ze strony mogą wpływać na LCP, te 2 – zwłaszcza czas rozpoczęcia i zakończenia zasobu LCP – wskazują, czy strona jest zoptymalizowana pod kątem LCP.

Do identyfikacji zasobu LCP możesz użyć narzędzi dla programistów (takich jak omówione wcześniej narzędzie PageSpeed Insights, Narzędzi deweloperskich w Chrome lub WebPageTest). Następnie możesz dopasować adres URL (ponownie, jeśli ma to zastosowanie) wczytywany przez element z kaskady sieci wszystkich zasobów wczytywanych przez stronę.

Na przykład ta wizualizacja pokazuje te zasoby wyróżnione na diagramie kaskady sieci podczas typowego wczytywania strony, gdzie element LCP wymaga przesłania żądania obrazu.

Kaskada sieci z wyróżnionymi zasobami HTML i LCP
Schemat kaskady przedstawiający czasy wczytywania dla w kodzie HTML strony i zasobach wymaganych przez LCP.

W przypadku dobrze zoptymalizowanej strony żądanie zasobu LCP powinno zacząć ładować się jak najwcześniej oraz gdy element LCP zostanie wyrenderowany jak najszybciej po zakończeniu wczytywania zasobu LCP. Aby ułatwić wizualizację tego, czy konkretna strona postępuje zgodnie z tą zasadą, możesz podzielić łączny czas LCP na następujące części:

Czas do pierwszego bajtu (TTFB)
Czas od inicjowania wczytywania strony przez użytkownika do czasu, w którym przeglądarka otrzymuje pierwszy bajt odpowiedzi z dokumentu HTML.
Opóźnienie wczytywania zasobu
Czas między TTFB a rozpoczęciem wczytywania zasobu LCP przez przeglądarkę. Jeśli element LCP nie wymaga wczytania zasobu do renderowania (jeśli na przykład jest węzłem tekstowym renderowanym z użyciem czcionki systemowej). tym razem wynosi on 0.
Czas wczytywania zasobu
Czas potrzebny na wczytanie zasobu LCP. Jeśli LCP nie wymaga wczytania zasobu do renderowania, ten czas ma wartość 0.
Opóźnienie renderowania elementu
Czas, jaki upływa od zakończenia wczytywania zasobu LCP do elementu LCP w pełni renderowanie.

LCP każdej strony składa się z tych 4 podkategorii. Nic się nie nakłada i nie ma żadnych pokrywających się segmentów i sumują się do łącznego czasu LCP.

Podział LCP na 4 podkategorie
Ten sam schemat kaskady z 4 podkategoriami LCP na osi czasu.

Wartość LCP każdej strony można podzielić na cztery części. Nie ma między nimi żadnych nakładania się ani różnic. Suma tych wartości jest równa pełnej wartości LCP.

Przy optymalizowaniu LCP warto spróbować zoptymalizować te części z osobna. Pamiętaj jednak, że musisz je wszystkie zoptymalizować. W niektórych przypadkach optymalizacja w jednej części nie poprawi LCP, a jedynie przeniesie zaoszczędzony czas w innej części.

Na przykład we wcześniejszej kaskadzie sieci – jeśli zmniejszysz rozmiar pliku obrazu przez większe skompresowanie lub przejście do bardziej optymalnego formatu (np. AVIF lub WebP), skróci to czas wczytywania zasobu, ale nie poprawi LCP, ponieważ czas ten zostanie przesunięty do podczęści opóźnienia renderowania elementu:

Ten sam rozkład LCP, w którym podkategoria czasu wczytywania zasobów jest skrócona, ale ogólny czas LCP pozostaje bez zmian.
Skrócenie czasu wczytywania zasobu zwiększa opóźnienie renderowania elementu bez zmniejszenia LCP.

Dzieje się tak, ponieważ element LCP na tej stronie jest ukryty do czasu zakończenia wczytywania kodu JavaScript, a potem zostaje natychmiast ujawniony.

Ten przykład pokazuje, dlaczego należy zoptymalizować wszystkie te części, aby uzyskać jak najlepsze wyniki LCP.

Optymalne czasy części składowych

Aby zoptymalizować każdą część LCP, trzeba wiedzieć, jak idealny rozkład tych części jest odpowiedni na dobrze zoptymalizowanej stronie.

Dwie z czterech części noszą słowo „opóźnienie”. w ich nazwach. Dlatego warto zadbać o to, aby wartości były jak najbardziej zbliżone do zera. Pozostałe 2 części to żądania sieciowe, które z natury są bardzo czasochłonne.

Część podrzędna LCP % LCP
Czas do pierwszego bajtu ok. 40%
Opóźnienie wczytywania zasobu <10%
Czas wczytywania zasobu ok. 40%
Opóźnienie renderowania elementu <10%
RAZEM 100%

Pamiętaj, że podane przedziały czasowe są wskazówkami, a nie ścisłymi regułami. Jeśli wartości LCP na stronach wynoszą stabilnie 2,5 sekundy, te proporcje nie mają znaczenia. Jeśli jednak spędzasz dużo czasu w opóźnieniach, bardzo trudno będzie osiągnąć cel 2, 5 sekundy.

Oto prawidłowy sposób przedstawienia rozkładu czasu LCP:

  • Zdecydowaną większość czasu LCP należy poświęcić na wczytywanie dokumentu HTML i źródła LCP.
  • Każdy moment przed LCP, w którym jeden z tych 2 zasobów nie się wczytuje, stanowi szansę na ulepszenie.
.

Jak zoptymalizować każdą część

Wiesz już, jak wygląda rozkład poszczególnych podczęści LCP na dobrze zoptymalizowanej stronie. Teraz możesz zacząć optymalizować swoje strony.

Kolejne cztery sekcje zawierają zalecenia i sprawdzone metody optymalizacji każdej części. Są one prezentowane w odpowiedniej kolejności, zaczynając od optymalizacji, która prawdopodobnie będzie najbardziej skuteczna.

1. Eliminowanie opóźnienia wczytywania zasobów

W tym kroku należy zadbać o to, aby zasób LCP jak najwcześniej zaczął się ładować. Chociaż w teorii najwcześniej możliwy do rozpoczęcia ładowania zasobu jest tuż po TTFB, w praktyce zawsze występuje pewne opóźnienie, zanim przeglądarki zaczną ładować zasoby.

Jako zasadę można przyjąć, że ładowanie zasobu LCP powinno rozpocząć się w tym samym czasie co pierwszy zasób wczytany przez daną stronę. Inaczej mówiąc, jeśli zasób LCP zacznie się wczytywać później niż pierwszy zasób, będzie można go ulepszyć.

Schemat kaskady sieci przedstawiający zasób LCP rozpoczynający się po pierwszym zasobie, co pokazuje możliwość poprawy
Na tej stronie zasób LCP zaczyna ładować się odpowiednio po zastosowaniu stylu arkusza, który zostanie wczytany jako pierwszy. Co można poprawić.

Ogólnie rzecz biorąc, czas wczytywania zasobu LCP zależy od 2 czynników:

  • Gdy zasób zostanie wykryty.
  • Priorytet, który ma mieć zasób.

Optymalizuj po wykryciu zasobu

Aby zasób LCP ładował się jak najwcześniej, musi on być wykrywalny w początkowej odpowiedzi dokumentu HTML przez skaner wstępnego wczytywania przeglądarki. Na przykład w następujących przypadkach przeglądarka może wykryć zasób LCP, skanując odpowiedź dokumentu HTML:

  • Element LCP jest elementem <img>, a jego atrybuty src i srcset znajdują się w początkowych znacznikach HTML.
  • Element LCP wymaga obrazu tła CSS, ale jest on wstępnie wczytywany za pomocą <link rel="preload"> w znacznikach HTML (lub za pomocą nagłówka Link).
  • Element LCP to węzeł tekstowy, który wymaga do renderowania czcionki internetowej, a czcionka jest ładowana za pomocą <link rel="preload"> w znacznikach HTML (lub za pomocą nagłówka Link).

Oto kilka przykładów sytuacji, w których nie można wykryć zasobu LCP podczas skanowania odpowiedzi dokumentu HTML:

  • Element LCP to identyfikator <img>, który jest dynamicznie dodawany do strony za pomocą JavaScriptu.
  • Element LCP jest leniwie ładowany za pomocą biblioteki JavaScript, która ukrywa atrybuty src lub srcset (często jako data-src lub data-srcset).
  • Element LCP wymaga obrazu tła CSS.

W każdym z tych przypadków przeglądarka musi uruchomić skrypt lub zastosować arkusz stylów (co zazwyczaj wiąże się z oczekiwaniem na zakończenie żądań sieciowych), zanim wykryje zasób LCP i będzie mógł rozpocząć jego wczytywanie. To nigdy nie jest optymalne.

Aby wyeliminować niepotrzebne opóźnienie wczytywania zasobów, zasób LCP powinien być wykrywalny w źródle HTML. Jeśli do zasobu odwołuje się tylko zewnętrzny plik CSS lub JavaScript, zasób LCP powinien być wstępnie wczytywany z wysokim priorytetem pobierania, na przykład:

<!-- Load the stylesheet that will reference the LCP image. -->
<link rel="stylesheet" href="/path/to/styles.css">

<!-- Preload the LCP image with a high fetchpriority so it starts loading with the stylesheet. -->
<link rel="preload" fetchpriority="high" as="image" href="/path/to/hero-image.webp" type="image/webp">

Zoptymalizuj priorytet przydzielanego zasobu

Nawet jeśli zasób LCP jest wykrywalny za pomocą znaczników HTML, nadal może się on zacząć ładować dopiero od pierwszego zasobu. Może się tak zdarzyć, gdy heurystyka priorytetowa skanera w przeglądarce nie rozpozna, że zasób jest ważny, lub jeśli ustali, że inne zasoby są ważniejsze.

Możesz np. opóźnić wyświetlanie obrazu LCP za pomocą kodu HTML, jeśli w elemencie <img> ustawisz loading="lazy". Użycie leniwego ładowania oznacza, że zasób zostanie wczytany dopiero wtedy, gdy układ potwierdzi, że obraz znajduje się w widocznym obszarze. W rezultacie ładowanie może rozpocząć się później niż zwykle.

Nawet bez leniwego ładowania obrazy nie są początkowo wczytywane z najwyższym priorytetem, ponieważ nie są to zasoby blokujące renderowanie. Możesz wskazać przeglądarce zasoby, które są najważniejsze, korzystając z atrybutu fetchpriority dla zasobów, które mogłyby skorzystać na podniesieniu priorytetu:

<img fetchpriority="high" src="/path/to/hero-image.webp">

Warto ustawić fetchpriority="high" dla elementu <img>, jeśli uważasz, że może to być element LCP Twojej strony. Jednak ustawienie wysokiego priorytetu więcej niż 1 lub 2 obrazów sprawia, że ustawienie priorytetu nie pomaga w zmniejszeniu LCP.

Możesz też obniżyć priorytet obrazów, które mogą być na początku odpowiedzi dokumentu, ale nie są widoczne ze względu na ich styl – na przykład obrazów w karuzeli, które nie są widoczne podczas uruchamiania:

<img fetchpriority="low" src="/path/to/carousel-slide-3.webp">

Zmniejszenie priorytetu niektórych zasobów może zapewnić większą przepustowość dla zasobów, które ich bardziej potrzebują, ale zachowaj ostrożność. Zawsze sprawdzaj priorytet zasobów w Narzędziach deweloperskich i testuj zmiany za pomocą narzędzi laboratoryjnych i terenowych.

Po zoptymalizowaniu priorytetu i czasu wykrywania zasobu LCP Twoja kaskada sieci powinna wyglądać następująco (zasób LCP rozpoczyna się w tym samym czasie co pierwszy zasób):

Schemat kaskady sieci przedstawiający zasób LCP, który zaczyna się w tym samym czasie co pierwszy zasób
Zasób LCP zaczyna się teraz wczytywać w tym samym czasie co arkusz stylów.
.

2. Eliminowanie opóźnienia renderowania elementu.

Celem tego kroku jest zagwarantowanie, że element LCP będzie mógł się wyrenderować od razu po zakończeniu wczytywania jego zasobu, niezależnie od tego, kiedy to nastąpi.

Głównym powodem, dla którego element LCP nie byłby w stanie wyrenderować się od razu po zakończeniu wczytywania zasobu, to jest zablokowanie renderowania z innego powodu:

  • Renderowanie całej strony jest zablokowane z powodu nadal wczytywanych arkuszy stylów lub skryptów synchronicznych w elemencie <head>.
  • Zasób LCP został wczytany, ale element LCP nie został jeszcze dodany do modelu DOM (czeka na wczytanie kodu JavaScript).
  • Element jest ukryty przez inny kod, np. bibliotekę testów A/B, która nadal określa, w którym eksperymencie powinien uczestniczyć użytkownik.
  • Wątek główny jest zablokowany z powodu długich zadań, a renderowanie musi zaczekać na zakończenie tych długich zadań.

W sekcjach poniżej wyjaśniamy, jak radzić sobie z najczęstszymi przyczynami niepotrzebnego opóźnienia renderowania elementu.

Ogranicz lub wbudowane arkusze stylów blokujące renderowanie

Arkusze stylów wczytane za pomocą znaczników HTML będą blokować renderowanie całej treści, która następuje po nich, co jest dobrą zasadą, ponieważ zazwyczaj nie chcesz renderować kodu HTML bez stylu. Jeśli jednak arkusz stylów jest tak duży, że wczytywanie trwa znacznie dłużej niż zasób LCP, uniemożliwi to wyrenderowanie elementu LCP – nawet po zakończeniu wczytywania zasobu, jak pokazano w tym przykładzie:

Diagram kaskady sieci przedstawiający blokowanie renderowania elementu LCP przez duży plik CSS, ponieważ wczytywanie trwa dłużej niż zasób LCP
Wczytywanie obrazu i arkusza stylów rozpoczyna się jednocześnie, ale nie można go wyrenderować, dopóki arkusz stylów nie będzie gotowy.

Aby rozwiązać ten problem, wykonaj jedną z tych czynności:

  • wstawić arkusz stylów do kodu HTML, by uniknąć dodatkowego żądania sieciowego; lub,
  • aby zmniejszyć rozmiar arkusza stylów.

Ogólnie zalecamy wbudowanie arkusza stylów tylko wtedy, gdy jest on mały, ponieważ treść wbudowana w kodzie HTML nie może korzystać z buforowania przy kolejnych wczytywaniu stron. Jeśli arkusz stylów jest tak duży, że jego wczytywanie trwa dłużej niż zasób LCP, raczej nie nadaje się do wbudowania.

W większości przypadków najlepszym sposobem, aby arkusz stylów nie blokował renderowania elementu LCP, jest zmniejszenie jego rozmiaru, tak aby był mniejszy niż zasób LCP. Dzięki temu nie stanowi ono wąskiego gardła dla większości wizyt.

Oto kilka zaleceń dotyczących zmniejszenia rozmiaru arkusza stylów:

Odrocz lub wbudowany kod JavaScript blokujący renderowanie

Prawie nigdy nie trzeba dodawać skryptów synchronicznych (skryptów bez atrybutów async lub defer) do tagu <head> stron, więc prawie zawsze będzie miało to negatywny wpływ na wydajność.

Jeśli kod JavaScript musi być uruchamiany jak najwcześniej podczas wczytywania strony, najlepiej go umieścić w tekście, aby renderowanie nie było opóźnione w oczekiwaniu na kolejne żądanie sieciowe. Podobnie jak w przypadku arkuszy stylów, należy jednak stosować tylko bardzo małe skrypty wbudowane.

Nie
<head>
  <script src="/path/to/main.js"></script>
</head>
Tak
<head>
  <script>
    // Inline script contents directly in the HTML.
    // IMPORTANT: only do this for very small scripts.
  </script>
</head>

Użyj renderowania po stronie serwera

Renderowanie po stronie serwera (SSR) to proces uruchamiania logiki aplikacji po stronie klienta na serwerze i odpowiadania na żądania dokumentów HTML za pomocą pełnych znaczników HTML.

Jeśli chodzi o optymalizację LCP, SSR ma 2 główne zalety:

  • Zasoby obrazów będzie można znaleźć w źródle HTML (jak omówiono w kroku 1 wcześniej).
  • Wyrenderowanie treści strony nie będzie wymagało żadnych dodatkowych żądań JavaScriptu przed jej wyrenderowaniem.

Główną wadą SSR jest konieczność wydłużenia czasu przetwarzania danych, co może spowolnić działanie funkcji TTFB. Takie rozwiązanie jest zwykle opłacalne, ponieważ czas przetwarzania serwera jest w zasięgu Twojej kontroli, podczas gdy na możliwości sieci i urządzeń użytkowników nie masz wpływu.

Opcja podobna do SSR nosi nazwę statycznego generowania witryny (SSG) lub renderowania wstępnego. Jest to proces generowania stron HTML na etapie kompilacji, a nie na żądanie. Jeśli Twoja architektura umożliwia renderowanie wstępne, zwykle lepiej jest wybrać je pod kątem wydajności.

Podziel długie zadania

Nawet jeśli po zastosowaniu się do wcześniejszych wskazówek kod JavaScript nie blokuje renderowania i nie jest odpowiedzialny za renderowanie elementów, nadal może opóźniać LCP.

Najczęstszą przyczyną takiej sytuacji jest wczytywanie dużych plików JavaScript, które muszą zostać przeanalizowane i wykonane w głównym wątku przeglądarki. Oznacza to, że nawet wtedy, gdy zasób obrazu został w pełni pobrany, przed renderowaniem może on nadal czekać na zakończenie wykonywania niepowiązanego skryptu.

Obecnie wszystkie przeglądarki renderują obrazy w wątku głównym, co oznacza, że wszystko, co blokuje wątek główny, może też prowadzić do niepotrzebnego opóźnienia renderowania elementu.

3. Skrócenie czasu wczytywania zasobów

Celem tego kroku jest skrócenie czasu poświęcanego na przenoszenie bajtów zasobu przez sieć na urządzenie użytkownika. Ogólnie można to zrobić na 3 sposoby:

  • Zmniejsz rozmiar zasobu.
  • Zmniejsz odległość, jaką musi pokonać zasób.
  • Zmniejsz rywalizację o przepustowość sieci.
  • Całkowicie wyeliminuj czas korzystania z sieci.

Zmniejsz rozmiar zasobu

Zasobem LCP strony (jeśli taki istnieje) jest obraz lub czcionka internetowa. W tych przewodnikach szczegółowo opisujemy, jak zmniejszyć rozmiar obu tych elementów:

Zmniejsz odległość, jaką musi pokonać zasób

Poza zmniejszeniem rozmiaru zasobu można także zmniejszyć czas wczytywania przez ustawienie serwerów jak największej odległości od użytkowników. Najlepszym sposobem jest skorzystanie z sieci dostarczania treści (CDN).

Szczególnie przydatne są sieci CDN z obrazami, ponieważ nie tylko zmniejszają odległość, jaką musi pokonać zasób, ale też ogólnie zmniejszają jego rozmiar, ponieważ automatycznie wdrażają wszystkie wcześniejsze rekomendacje dotyczące zmniejszenia rozmiaru.

Zmniejsz rywalizację o przepustowość sieci

Nawet jeśli zmniejszysz rozmiar zasobu i zmniejszysz jego odległość, wczytywanie zasobu może jeszcze długo trwać, jeśli wczytujesz wiele innych zasobów jednocześnie. Ten problem jest znany jako rywalizacja sieci.

Jeśli zasób LCP ma wysoki poziom fetchpriority i rozpocznie się ładowanie tak szybko, jak to możliwe, przeglądarka będzie starała się zapobiec konkurowaniu z nim zasobów o niższym priorytecie. Jeśli jednak wczytujesz wiele zasobów z wysokim wskaźnikiem fetchpriority lub jeśli wczytujesz tylko dużo zasobów, może to mieć wpływ na szybkość wczytywania zasobu LCP.

Całkowite wyeliminowanie czasu korzystania z sieci

Najlepszym sposobem na skrócenie czasu obciążenia zasobów jest całkowite wykluczenie sieci z procesu. Jeśli obsługujesz zasoby, stosując efektywną zasadę kontroli pamięci podręcznej, użytkownicy, którzy zażądają tych zasobów po raz drugi, będą mogli je wyświetlać z pamięci podręcznej, co spowoduje skrócenie czasu wczytywania zasobu do zera.

Jeśli zasób LCP to czcionka internetowa, oprócz zmniejszenia rozmiaru czcionki internetowej musisz też wziąć pod uwagę, czy nie musisz blokować renderowania podczas wczytywania tych zasobów. Jeśli w polu font-display ustawisz wartość inną niż auto lub block, tekst będzie zawsze widoczny podczas wczytywania, a LCP nie będzie blokowany po otrzymaniu dodatkowego żądania sieciowego.

I wreszcie, jeśli zasób LCP jest mały, warto je wbudować w adres URL danych, co pozwoli uniknąć dodatkowego żądania sieciowego. Stosowanie takich adresów ma jednak pewne ograniczenia, ponieważ zasobów nie można przechowywać w pamięci podręcznej, a w niektórych przypadkach może to prowadzić do dłuższych opóźnień renderowania ze względu na dodatkowe koszty dekodowania.

4. Skróć czas do pierwszego bajtu

Celem tego etapu jest jak najszybsze dostarczenie początkowego kodu HTML. Ten krok jest wymieniony na końcu, ponieważ często nad nim deweloper ma najmniejszą kontrolę. Jest to jednak również jeden z najważniejszych kroków, ponieważ wpływa bezpośrednio na każdy kolejny krok. Dopóki backend nie dostarczy pierwszego bajtu treści, nic się nie wydarzy, więc wszystko, co możesz zrobić, aby przyspieszyć TTFB, poprawi też wszystkie inne wskaźniki wczytywania.

Częstą przyczyną powolnego działania funkcji TTFB w przypadku witryn, które są niezbyt szybko szybkie, są wielokrotne przekierowania, np. z reklam lub skróconych linków. Zawsze minimalizuj liczbę przekierowań, które musi przejść użytkownik.

Inną częstą przyczyną jest sytuacja, w której nie można użyć zawartości pamięci podręcznej z serwera brzegowego CDN i wszystkie żądania muszą być kierowane z powrotem do serwera pierwotnego. Może się tak zdarzyć, jeśli unikalne parametry adresu URL będą wykorzystywane przez użytkowników do celów analitycznych – nawet jeśli nie prowadzą do różnych stron.

Szczegółowe wskazówki dotyczące optymalizacji TTFB znajdziesz w przewodniku optymalizacji TTFB.

Monitorowanie podziału LCP w JavaScripcie

Informacje o czasie dla wszystkich omówionych wcześniej podpunktów LCP są dostępne w języku JavaScript za pomocą kombinacji tych interfejsów API do kontrolowania wydajności:

Zaletą obliczania wartości czasowych w języku JavaScript jest to, że można je wysłać do dostawcy usług analitycznych lub zapisać je w narzędziach programistycznych, aby ułatwić debugowanie i optymalizację.

Na przykład ten zrzut ekranu korzysta z metody performance.measure() z interfejsu User Timing API, aby dodać słupki do ścieżki czasu w panelu Wydajność Narzędzi deweloperskich w Chrome.

Pomiary czasu użytkownika dotyczące podkategorii LCP wizualizowanych w Narzędziach deweloperskich w Chrome
Ścieżka Czasy pokazuje osie czasu dla podkategorii LCP.

Wizualizacje na ścieżce Czasy są szczególnie przydatne, gdy analizuje się je równolegle ze ścieżkami Sieć i Wątek główny, ponieważ pozwalają szybko sprawdzić, co jeszcze dzieje się na stronie w tych przedziałach czasu.

Oprócz wizualizacji podczęści LCP na ścieżce czasu możesz też za pomocą JavaScriptu obliczyć, jaki procent poszczególnych części składowych LCP składa się z całkowitego czasu LCP. Na podstawie tych informacji możesz określić, czy Twoje strony spełniają podane wcześniej zalecane zestawienie procentowe.

Na tym zrzucie ekranu widać przykład rejestrowania w konsoli łącznego czasu każdego podpunktu LCP oraz jego procentowego udziału w łącznym czasie LCP.

Godziny podkategorii LCP i ich odsetek LCP, które są wyświetlane w konsoli.
Czasy i wartości procentowe podkategorii LCP.

Obie wizualizacje zostały utworzone za pomocą tego kodu:

const LCP_SUB_PARTS = [
  'Time to first byte',
  'Resource load delay',
  'Resource load duration',
  'Element render delay',
];

new PerformanceObserver((list) => {
  const lcpEntry = list.getEntries().at(-1);
  const navEntry = performance.getEntriesByType('navigation')[0];
  const lcpResEntry = performance
    .getEntriesByType('resource')
    .filter((e) => e.name === lcpEntry.url)[0];

  // Ignore LCP entries that aren't images to reduce DevTools noise.
  // Comment this line out if you want to include text entries.
  if (!lcpEntry.url) return;

  // Compute the start and end times of each LCP sub-part.
  // WARNING! If your LCP resource is loaded cross-origin, make sure to add
  // the `Timing-Allow-Origin` (TAO) header to get the most accurate results.
  const ttfb = navEntry.responseStart;
  const lcpRequestStart = Math.max(
    ttfb,
    // Prefer `requestStart` (if TOA is set), otherwise use `startTime`.
    lcpResEntry ? lcpResEntry.requestStart || lcpResEntry.startTime : 0
  );
  const lcpResponseEnd = Math.max(
    lcpRequestStart,
    lcpResEntry ? lcpResEntry.responseEnd : 0
  );
  const lcpRenderTime = Math.max(
    lcpResponseEnd,
    // Use LCP startTime (the final LCP time) because there are sometimes
    // slight differences between loadTime/renderTime and startTime
    // due to rounding precision.
    lcpEntry ? lcpEntry.startTime : 0
  );

  // Clear previous measures before making new ones.
  // Note: due to a bug, this doesn't work in Chrome DevTools.
  LCP_SUB_PARTS.forEach((part) => performance.clearMeasures(part));

  // Create measures for each LCP sub-part for easier
  // visualization in the Chrome DevTools Performance panel.
  const lcpSubPartMeasures = [
    performance.measure(LCP_SUB_PARTS[0], {
      start: 0,
      end: ttfb,
    }),
    performance.measure(LCP_SUB_PARTS[1], {
      start: ttfb,
      end: lcpRequestStart,
    }),
    performance.measure(LCP_SUB_PARTS[2], {
      start: lcpRequestStart,
      end: lcpResponseEnd,
    }),
    performance.measure(LCP_SUB_PARTS[3], {
      start: lcpResponseEnd,
      end: lcpRenderTime,
    }),
  ];

  // Log helpful debug information to the console.
  console.log('LCP value: ', lcpRenderTime);
  console.log('LCP element: ', lcpEntry.element, lcpEntry.url);
  console.table(
    lcpSubPartMeasures.map((measure) => ({
      'LCP sub-part': measure.name,
      'Time (ms)': measure.duration,
      '% of LCP': `${
        Math.round((1000 * measure.duration) / lcpRenderTime) / 10
      }%`,
    }))
  );
}).observe({type: 'largest-contentful-paint', buffered: true});

Możesz użyć tego kodu w niezmienionej formie do lokalnego debugowania lub zmodyfikować go, aby wysyłać te dane do dostawcy usług analitycznych, co pozwoli Ci lepiej zrozumieć, jak duży jest LCP na Twoich stronach dla rzeczywistych użytkowników.

Monitorowanie zestawienia LCP za pomocą rozszerzenia Web Vitals

Rozszerzenie Web Vitals rejestruje czas LCP, element LCP i te 4 podczęści w konsoli, aby umożliwić wyświetlenie tego podziału.

Logowanie w konsoli rozszerzenia do wskaźników internetowych pokazujące czasy podczęści LCP
Panel konsoli internetu Rozszerzenie Vitals pokazuje zestawienie LCP.

Podsumowanie

LCP to złożony proces, a jego czas może zależeć od wielu czynników. Jeśli jednak weźmiemy pod uwagę, że optymalizacja LCP polega głównie na optymalizacji obciążenia zasobów LCP, może to znacznie uprościć cały proces.

Ogólnie rzecz biorąc, optymalizację LCP można podsumować w 4 krokach:

  1. Upewnij się, że ładowanie zasobu LCP rozpoczyna się jak najwcześniej.
  2. Sprawdź, czy element LCP może się wyrenderować zaraz po zakończeniu wczytywania jego zasobu.
  3. Skróć czas wczytywania zasobu LCP jak najdokładniej bez negatywnego wpływu na jakość.
  4. Dostarcz początkowy dokument HTML tak szybko, jak to możliwe.

Jeśli jesteś w stanie wykonać te czynności na swoich stronach, masz pewność, że zapewniasz użytkownikom optymalną wygodę wczytywania, co powinno zostać odzwierciedlone w rzeczywistych wynikach LCP.