Renderowanie kodu HTML za pomocą JavaScriptu różni się od renderowania kodu HTML wysyłanego przez serwer i może mieć wpływ na wydajność. Z tego przewodnika dowiesz się, czym się różnią, i co możesz zrobić, aby zachować wydajność renderowania witryny, zwłaszcza w przypadku interakcji.
Analizowanie i renderowanie kodu HTML to coś, co przeglądarki domyślnie robią bardzo dobrze w przypadku witryn korzystających z wbudowanej logiki nawigacji przeglądarki, zwanej czasem „tradycyjnym wczytywaniem stron” lub „twardą nawigacją”. Takie witryny są czasami nazywane aplikacjami wielostronicowymi (MPA).
Deweloperzy mogą jednak obejść ustawienia domyślne przeglądarki, aby dostosować je do potrzeb aplikacji. Dotyczy to zwłaszcza witryn korzystających z wzorca aplikacji jednostronicowej (SPA), który dynamicznie tworzy duże części kodu HTML/DOM na urządzeniu klienta za pomocą JavaScriptu. Ten wzorzec projektowy nazywa się renderowaniem po stronie klienta i może mieć wpływ na interakcję do kolejnego wyrenderowania (INP) w Twojej witrynie, jeśli wymaga zbyt dużo pracy.
Z tego przewodnika dowiesz się, jaka jest różnica między używaniem kodu HTML wysyłanego przez serwer do przeglądarki a tworzeniem go po stronie klienta za pomocą JavaScriptu, a także jak to drugie rozwiązanie może powodować duże opóźnienia interakcji w kluczowych momentach.
Jak przeglądarka renderuje kod HTML dostarczony przez serwer
W przypadku tradycyjnego wczytywania stron przy każdej nawigacji otrzymywany jest kod HTML z serwera. Jeśli wpiszesz adres URL na pasku adresu przeglądarki lub klikniesz link w MPA, nastąpi następująca seria zdarzeń:
- Przeglądarka wysyła żądanie nawigacji do podanego adresu URL.
- Serwer odpowiada kodem HTML w porcjach.
Ostatni z nich jest kluczowy. Jest to też jedna z najważniejszych optymalizacji skuteczności w wymianie danych między serwerem a przeglądarką, znana jako strumieniowanie. Jeśli serwer może jak najszybciej rozpocząć wysyłanie kodu HTML, a przeglądarka nie musi czekać na całą odpowiedź, może przetwarzać kod HTML w miarę jego docierania.

Podobnie jak większość działań w przeglądarce, parsowanie kodu HTML odbywa się w ramach zadań. Gdy kod HTML jest przesyłany strumieniowo z serwera do przeglądarki, przeglądarka optymalizuje jego analizowanie, robiąc to stopniowo, w miarę jak fragmenty strumienia docierają w postaci bloków. W rezultacie przeglądarka okresowo przekazuje kontrolę do wątku głównego po przetworzeniu każdego fragmentu, co pozwala uniknąć długich zadań. Oznacza to, że podczas analizowania kodu HTML mogą być wykonywane inne działania, w tym przyrostowe renderowanie niezbędne do wyświetlenia strony użytkownikowi, a także przetwarzanie interakcji użytkownika, które mogą wystąpić w kluczowym okresie uruchamiania strony. Takie podejście przekłada się na lepszy wynik interakcji do kolejnego wyrenderowania (INP) na stronie.
Wniosek? Podczas przesyłania strumieniowego HTML z serwera otrzymujesz przyrostowe parsowanie i renderowanie HTML oraz automatyczne przekazywanie do wątku głównego bez dodatkowych opłat. W przypadku renderowania po stronie klienta nie jest to możliwe.
Jak przeglądarka renderuje kod HTML dostarczony przez JavaScript
Każde żądanie przejścia na stronę wymaga, aby serwer dostarczył pewną ilość kodu HTML, ale niektóre witryny korzystają z wzorca SPA. To podejście często polega na tym, że serwer dostarcza minimalny początkowy ładunek HTML, a następnie klient wypełnia główny obszar treści strony kodem HTML złożonym z danych pobranych z serwera. Kolejne nawigacje – w tym przypadku czasami nazywane „miękkimi nawigacjami” – są w całości obsługiwane przez JavaScript, który wypełnia stronę nowym kodem HTML.
Renderowanie po stronie klienta może też występować w przypadku aplikacji innych niż SPA, ale w bardziej ograniczonych sytuacjach, gdy kod HTML jest dynamicznie dodawany do DOM za pomocą JavaScriptu.
Istnieje kilka typowych sposobów tworzenia kodu HTML lub dodawania go do DOM za pomocą JavaScriptu:
innerHTML
Właściwość umożliwia ustawienie treści w istniejącym elemencie za pomocą ciągu znaków, który przeglądarka analizuje w DOM.document.createElement
Metoda umożliwia tworzenie nowych elementów, które można dodać do DOM bez używania analizy HTML przeglądarki.document.write
Metoda umożliwia zapisywanie w dokumencie kodu HTML (który jest analizowany przez przeglądarkę, podobnie jak w przypadku metody 1). Z wielu powodów korzystanie zdocument.write
jest zdecydowanie odradzane.

Konsekwencje tworzenia kodu HTML/DOM za pomocą JavaScriptu po stronie klienta mogą być znaczące:
- W przeciwieństwie do kodu HTML przesyłanego strumieniowo przez serwer w odpowiedzi na żądanie nawigacji zadania JavaScript na kliencie nie są automatycznie dzielone na mniejsze części, co może powodować długie zadania blokujące wątek główny. Oznacza to, że INP strony może ulec pogorszeniu, jeśli w danym momencie tworzysz zbyt dużo kodu HTML lub DOM na urządzeniu klienta.
- Jeśli kod HTML jest tworzony na kliencie podczas uruchamiania, zasoby, do których się odwołuje, niezostaną wykryte przez skaner wstępnego wczytywania przeglądarki. Z pewnością będzie to miało negatywny wpływ na największe wyrenderowanie treści (LCP) strony. Nie jest to problem z wydajnością w czasie działania (zamiast tego jest to problem z opóźnieniem sieci w pobieraniu ważnych zasobów), ale nie chcesz, aby na LCP Twojej witryny wpływało omijanie tej podstawowej optymalizacji wydajności przeglądarki.
Co możesz zrobić, aby zmniejszyć wpływ renderowania po stronie klienta na wydajność
Jeśli Twoja witryna w dużej mierze zależy od renderowania po stronie klienta i w danych z terenu obserwujesz niskie wartości INP, możesz się zastanawiać, czy renderowanie po stronie klienta ma z tym coś wspólnego. Jeśli na przykład Twoja witryna to aplikacja SPA, dane z pola mogą ujawnić interakcje, które wymagają znacznego nakładu pracy związanej z renderowaniem.
Niezależnie od przyczyny poniżej znajdziesz kilka potencjalnych powodów, które pomogą Ci wrócić na właściwe tory.
Dostarcz jak najwięcej kodu HTML z serwera
Jak już wspomnieliśmy, przeglądarka domyślnie obsługuje kod HTML z serwera w bardzo wydajny sposób. Podzieli on analizowanie i renderowanie kodu HTML w taki sposób, aby uniknąć długich zadań i zoptymalizować łączny czas działania wątku głównego. Prowadzi to do niższego wskaźnika Total Blocking Time (TBT), który jest silnie skorelowany z INP.
Do tworzenia witryny możesz używać platformy frontendowej. W takim przypadku musisz mieć pewność, że renderujesz kod HTML komponentu na serwerze. Ograniczy to ilość początkowego renderowania po stronie klienta, jakiego będzie wymagać Twoja witryna, i powinno poprawić komfort korzystania z niej.
- W przypadku Reacta do renderowania kodu HTML na serwerze użyj interfejsu Server DOM API. Pamiętaj jednak, że tradycyjna metoda renderowania po stronie serwera wykorzystuje podejście synchroniczne, które może prowadzić do dłuższego czasu do pierwszego bajtu (TTFB), a także do kolejnych wskaźników, takich jak pierwsze wyrenderowanie treści (FCP) i LCP. W miarę możliwości używaj interfejsów API do przesyłania strumieniowego w przypadku Node.js lub innych środowisk wykonawczych JavaScript, aby serwer mógł jak najszybciej rozpocząć przesyłanie strumieniowe kodu HTML do przeglądarki. Next.js – framework oparty na React – domyślnie udostępnia wiele sprawdzonych metod. Oprócz automatycznego renderowania kodu HTML na serwerze może też statycznie generować kod HTML dla stron, które nie zmieniają się w zależności od kontekstu użytkownika (np. uwierzytelniania).
- Vue domyślnie wykonuje też renderowanie po stronie klienta. Podobnie jak React, Vue może też renderować kod HTML komponentu na serwerze. W miarę możliwości korzystaj z tych interfejsów API po stronie serwera lub rozważ zastosowanie w projekcie Vue abstrakcji wyższego poziomu, aby ułatwić wdrażanie sprawdzonych metod.
- Svelte domyślnie renderuje HTML na serwerze, ale jeśli kod komponentu wymaga dostępu do przestrzeni nazw dostępnych tylko w przeglądarce (np.
window
), renderowanie kodu HTML tego komponentu na serwerze może być niemożliwe. W miarę możliwości stosuj alternatywne podejścia, aby uniknąć niepotrzebnego renderowania po stronie klienta. SvelteKit, czyli to, czym Next.js jest dla Reacta, osadza w projektach Svelte jak najwięcej sprawdzonych metod, dzięki czemu możesz uniknąć potencjalnych pułapek w projektach, które korzystają tylko z Svelte.
Ograniczanie liczby węzłów DOM tworzonych na kliencie
Gdy DOM-y są duże, przetwarzanie wymagane do ich renderowania zwykle się zwiększa. Niezależnie od tego, czy Twoja witryna jest w pełni działającą aplikacją SPA, czy wstrzykuje nowe węzły do istniejącego DOM w wyniku interakcji w przypadku aplikacji MPA, staraj się, aby te DOM-y były jak najmniejsze. Pomoże to zmniejszyć nakład pracy wymagany podczas renderowania po stronie klienta w celu wyświetlenia kodu HTML, co powinno przyczynić się do utrzymania niższego wskaźnika INP witryny.
Rozważ architekturę skryptu service worker do strumieniowania
Jest to zaawansowana technika, która nie zawsze sprawdza się w każdym przypadku, ale może sprawić, że Twoja aplikacja wielostronicowa będzie działać jak witryna, która wczytuje się natychmiast, gdy użytkownicy przechodzą z jednej strony na drugą. Możesz użyć skryptu service worker do wstępnego buforowania statycznych części witryny w CacheStorage
, a do pobierania pozostałej części kodu HTML strony z serwera użyć ReadableStream
interfejsu API.
Jeśli ta technika zostanie zastosowana prawidłowo, nie będziesz tworzyć kodu HTML na urządzeniu klienta, ale natychmiastowe wczytywanie częściowych treści z pamięci podręcznej sprawi, że witryna będzie się wydawać szybko wczytywana. Witryny korzystające z tego podejścia mogą sprawiać wrażenie aplikacji SPA, ale bez wad renderowania po stronie klienta. Zmniejsza też ilość kodu HTML, o który prosisz serwer.
Krótko mówiąc, architektura streaming service worker nie zastępuje wbudowanej logiki nawigacji przeglądarki, ale ją uzupełnia. Więcej informacji o tym, jak to zrobić za pomocą Workbox, znajdziesz w artykule Szybsze aplikacje wielostronicowe dzięki strumieniom.
Podsumowanie
Sposób, w jaki witryna otrzymuje i renderuje kod HTML, ma wpływ na jej wydajność. Gdy polegasz na serwerze, który wysyła cały kod HTML (lub jego większość) potrzebny do działania witryny, zyskujesz wiele korzyści: przyrostowe parsowanie i renderowanie oraz automatyczne przekazywanie sterowania do wątku głównego, aby uniknąć długich zadań.
Renderowanie HTML po stronie klienta wiąże się z wieloma potencjalnymi problemami z wydajnością, których w wielu przypadkach można uniknąć. Ze względu na wymagania poszczególnych witryn nie zawsze można tego w 100% uniknąć. Aby ograniczyć potencjalne długie zadania, które mogą wynikać z nadmiernego renderowania po stronie klienta, w miarę możliwości wysyłaj z serwera jak najwięcej kodu HTML witryny, utrzymuj rozmiary DOM na jak najniższym poziomie w przypadku kodu HTML, który musi być renderowany po stronie klienta, i rozważ alternatywne architektury, aby przyspieszyć dostarczanie kodu HTML do klienta, a jednocześnie korzystać z przyrostowego parsowania i renderowania, które przeglądarka zapewnia w przypadku kodu HTML wczytywanego z serwera.
Jeśli zminimalizujesz renderowanie po stronie klienta w swojej witrynie, poprawisz nie tylko INP, ale też inne rodzaje danych, takie jak LCP, TBT, a w niektórych przypadkach nawet TTFB.
Baner powitalny z Unsplash, autor: Maik Jonietz.