SPA, SSG i SSR, czyli wielkie porównanie sposobu serwowania stron internetowych

Świat wypracował kilka różnych sposobów renderowania stron i serwowania ich dla użytkownika. Końcowym rezultatem jest strona internetowa wyrenderowana przez "czytanie" przez przeglądarkę kodu złożonego z trzech języków: HTML, CSS i JS. To, jak możemy to osiągnąć, zależy tylko od nas

Wprowadzenie

Potrzeba matką wynalazku, można by rzec. Na przestrzeni kilku ostatnich lat środowisko frontendu wypracowało wiele różnych metod i sposobów (m. in. przez nowoczesne frameworki) na rozwiązanie wówczas dość poważnych problemów związanych z renderowaniem stron, o których sobie powiemy. Wyróżniamy trzy sposoby serwowania stron:

  • generowanie statyczne (SSG - Static Site Generation)
  • renderowanie po stronie serwera (SSR - Server Side Rendering)
  • generowanie w locie (SPA - Single Page Application)

Na pewno nie można jednoznacznie powiedzieć, który z nich jest najlepszy. To, który sposób powinniśmy wybrać, zależy od wielu czynników (a przynajmniej tych, które warto wziąć pod uwagę zanim przystąpi się do tworzenia aplikacji), takich jak na przykład:

  • performance i SEO,
  • sposób serwowania danych z backendu,
  • transparentność wrażliwych danych (np. prywatne klucze API)
  • styl pisania aplikacji

Każdy sposób renderowania będzie charakteryzował się lepszym lub gorszym podejściem do powyższych problemów. Poruszę te kwestię w dwóch krokach: w teorii o danym sposobie serwowania stron oraz na przykładzie najpopularniejszych frameworków bazujących na danym sposobie renderowania. I tak dla SPA będzie to aplikacja reactowa (stworzona za pomocą create-react-app), dla SSG - Gatsby.js, a dla SSR - Next.js. Dlaczego akurat te narzędzia? Ponieważ odniosły ogromny sukces, jeżeli chodzi o frontend, znakomicie rozwiązują wiele problemów związanych z ich sposobem renderowania i zapewniają duży developer experience. Istnieje wiele innych odpowiedników dla tych frameworków. Podejrzewam jednak, że ich końcowy rezultat działania nie będzie się zbytnio różnił.

Single Page Application

Zacznijmy od sposobu generowania stron w locie. Ucząc się Reacta, zazwyczaj zaczyna się od tego sposobu renderowania (create-react-app). W praktyce polega on na tym, że kod wynikowy czytany przez przeglądarkę odbywa się za pomocą JavaScpriptu - po stronie klienta. Jak nietrudno się domyślić, ten sposób renderowania nie jest zbyt wydajny i na słabszych komputerach i telefonach można srogo to odczuć. Single Page Application to nazewnictwo ściśle związane z takim podejściem do renderowania wynikowego kodu. Oznacza to, że mamy jeden plik html (tytułowy single page), a do niego są wstrzykiwane kawałki kodu, który w danym momencie wywołujemy. Cała logika renderowania odbywa się za pomocą komputera użytkownika.

Plusem takiego rozwiązania jest łatwość pisania kodu - zazwyczaj w narzędziach SPA piszemy sam kod związany z Reactem i nie musimy zwracać uwagi na inne elementy abstrakcji charakterystycznych dla danego narzędzia, jak w Gatsbym i Nexcie. Tutaj ich po prostu nie ma. I to w zasadzie jest chyba jedynym plusem SPA.

Decydując się na SPA, musimy mieć świadomość, że nasze SEO będzie znikome - jedyną widoczną stroną zaindeksowaną w wyszukiwarkach będzie index.html. Router będzie renderował strony w JS, więc nie ma możliwości zaindeksowania innych stron, ponieważ wyszukiwarki tego po prostu nie "widzą". SPA nie nadaje się więc do aplikacji wymagających mocnego SEO (np. w przypadku e-commerce, gdzie każdą stronę produktu warto indeksować) lub do landing pages. Jako, że SPA jest w pełni kliencką aplikacją, to nie ukryjemy tutaj wrażliwych danych, takich jak klucze API, służących do integracji naszej apki z różnymi zewnętrznymi narzędziami. Żeby je ukryć potrzebujemy backendu. Wspomniałem także, że szybkość renderowania kodu i ogólny performance aplikacji nie będą zbyt dobre, a co za tym idzie, ponownie, indeksowanie wyszukiwarek (m. in. Google) nie będzie również zbyt dobre. W ostatnich latach zwraca się coraz większą uwagę na szybkość ładowania stron itd.

create-react-app

zdjęcie tematyczne

Wady i zalety SPA

Przedstawicielem rozwiązania Single Page Application jest narzędzie create-react-app. Korzystając z okazji, chciałem poruszyć jego temat. Niech pierwszy rzuci kamieniem ten, który nie zaczął nauki Reacta od CRA. Ja sam popełniłem ten błąd, czego nieco żałuję. Większość kursów zmusza nas na wskoczenie od początku na CRA i naukę Reacta razem z tym narzędziem. Nie uważam, że jest to dobra decyzja. Powinniśmy zaczynać poznawać tę bibliotekę od instalowania jej standalone w aplikacji HTML-CSS-JS, ewentualnie rozszerzając nasze środowisko o bundlery, pomniejsze biblioteki itd., tym samym budując nasze własne CRA. Dopiero później, wiedząc jak to wszystko działa, moglibyśmy pozwolić sobie na używanie gotowej biblioteki, w której wszystko otrzymujemy by default. Takie podejście dużo by nas uczyło, a w przeciwnym wypadku uczymy się Reacta, który jest tylko Reactem (złudnie), a nie całym ekosystemem, który musi być bundlowany i spajany przez webacka oraz masę innych bilbiotek node. Wspominałem o tym w artykule o Node.js.

Zalety:

  • łatwość pisania kodu

Wady:

  • Praktycznie zerowe SEO
  • Renderowanie aplikacji w całości przez klienta (użytkownika)
  • Bardzo słaba wydajność (a przynajmniej najgorsza ze wszystkich dostępnych opcji)
  • Brak możliwości ukrycia wrażliwych danych (przez brak backendu / środowiska Node)

Kiedy warto używać:

  • Do nauki stricte frameworka / bilbioteki, na którym oparte jest SPA
  • Do zamkniętych interfejsów przeznaczonych na komputery (rzadziej telefony - wtedy warto pomyśleć o aplikacji mobilnej), np. panel do administrowania jakimś produktem, podmiotem, zamkniętym za panelem logowania. W takich przypadkach zazwyczaj nie potrzeba żadnego SEO

Static Site Generation

Najszybszy i najwydajniejszy sposób serwowania stron. W praktyce wygląda tak, że serwer przesyła gotowe już pliki html na komputer użytkownika strony. Wchodząc na daną stronę, klient po prostu odpytuje serwer poprzez protokół HTTP, czy dana strona istnieje i poprzez sieć ją wysyła w postaci fizycznego pliki. W tym wypadku nic nie musi być generowane w locie, ani po stronie serwera. To tak jakbyśmy tworzyli osobny plik html do każdej strony. W praktyce oczywiście tak nie robimy (no chyba że ktoś bardzo chce, ale wtedy nie potrzebujemy do tego żadnych frameworków), ale na tym ten sposób w istocie opiera swoje działanie i końcowo do takich plików się sprowadza. Jasnym jest więc, że taka aplikacja / strona będzie działać bardzo szybko.

Gatsby.js

zdjęcie tematyczne

Przedstawicielem SSG jest framework Gatsby.js. Działa on na Reakcie i generuje strony statyczne, jakie tylko chcemy. Co prawda nie posiada on serwera, ale za pomocą środowiska Node rozwiązuje część problemów, z którymi nie radzi sobie SPA. Otóż Gatsby automatycznie generuje statyczne pliki html w momencie budowy aplikacji (w momencie wpisania w konsoli komendy gatsby build). Oznacza to, że mając np. 200 wpisów na blogu lub 200 produktów w sklepie, ich odpowiednie strony html są generowane w momencie budowy aplikacji i nigdy więcej. Dane te mogą być zaciągane z zewnętrznego CMSa, JSONa umieszczonego w workspace projektu lub nawet z plików markdown. W przypadku kiedy dodajemy nowy wpis lub produkt, aplikacja musi być ponownie zbudowana i zdeployowana na serwer. Oczywiście można zaciągać dane i tworzyć zależności po stronie klienta w Gatsbym, ale wtedy te części aplikacji stają się jakoby klasycznym SPA i na pewno pogarsza to wydajność. Na pewno Gatsby sprawdzi się wszędzie tam, gdzie dane z backendu nie zmieniają się zbyt często oraz nie musimy mutować (aktualizować itd.) tych danych aktualnie już zaciągniętych podczas buildu aplikacji.

Istota Gatsbyego polega na tym, że budowa aplikacji opartej na tym frameworku sprowadza się do stworzenia prostych plików html, które użytkownik będzie mógł zaciągać do przeglądarki. Klucze API mogą być przechowywane w środowisku Node, które jest wykorzystywane w momencie budowy aplikacji (requesty są wykonywane tylko raz, więc po stronie klienta nie muszą już być widoczne), a większość hostingów wspierających narzędzia SSG umożliwia tworzenie zmiennych środowiskowych zastępujących plink .env, który swoją drogą potrzebny jest w zasadzie tylko lokalnie).

Wady i zalety SSG

Zalety:

  • ogromna wydajność
  • z uwagi na dobry performance i statyczne pliki html, najlepsze wsparcie dla SEO
  • możliwość ukrycia wrażliwych danych w pliku .env, z którego pobierane są dane tylko i wyłącznie w momencie budowania aplikacji w środowisku node
  • w przypadku Gatsbyego, ogromna ilość pluginów i wsparcie społeczności

Wady:

  • dodatkowa warstwa abstrakcji do nauki i obycia
  • w momencie kiedy chcemy zrobić coś więcej, np. wykonywać requesty po stronie klienta, to Gatsby otrzymuje niektóre problemy znane z SPA

Kiedy warto używać:

  • wszelkie landing pages, strony wizytówki, blogi itp., gdzie występują znikome ilość danych z API
  • dokumentacje narzędzi i bibliotek
  • wszędzie tam gdzie nie musimy mutować danych istniejących w bazie, ani pobierać danych co chwilę

Server Side Rendering

Jak sama nazwa wskazuje, w tym przypadku wynikowy kod jest renderowany po stronie serwera i wysyłany gotowy do klienta. Można trochę nagiąć rzeczywistość i powiedzieć, że jest to takie SPA dla serwera, gdzie komputer klienta nie musi tego robić sam, a jedynie szybki komputer serwerowy, gdzieś daleko, który serwuje nam to, co chcemy. Co nam to daje? Po pierwsze rozwiązuje to problem SEO. Mimo że te strony fizycznie nie są generowane, to posiadanie logiki SSR na serwerze pozwala na oszukanie nieco wyszukiwarek i robotów, które analizują sieć, w rezultacie czego widzą te strony jakby fizycznie istniały. Ponadto, nie musimy martwić się tutaj o renderowanie kodu, więc i wydajność będzie dobra, choć nie tak dobra jak przy Gatsbym.

SSR daje nam również backend z prawdziwego zdarzenia, co jest chyba największą jego zaletą. Możemy wysyłać requesty po stronie backendu, odbierać te dane, a użytkownikowi serwować tylko te dane, które ogarnął serwer. Daje nam to ogromne możliwości - możemy nawet wystawiać własne endpointy lub czytać pliki na serwerze za każdym wejściem użytkownika na stronę. Oczywiście wszystko zależy od naszych potrzeb i wymagań, ale w tym przypadku ukrycie zmiennych i danych wrażliwych nie będzie żadnym problemem. Oczywiście zawsze jest jakiś tradeoff, więc gdzie jest haczyk? Mieszanie środowiska node (na którym stoi serwer) wraz z środowiskiem klienckim, z API dostępnym w przeglądarkach jest czasami problematyczne. Rośnie również poziom abstrakcji - taki framework jak NEXT.js dodaje sporo do nauki przez nową składnię. Oprócz tego, musimy jakość zahostować aplikację SSR, w 90% przypadkach bez Dockera się więc nie obejdzie.

Next.js

zdjęcie tematyczne

Next.js - przedstawiciel SSR, który dość szybko rozwija się w ostatnich latach, zapewnia nam sporą dawkę nowych rzeczy do nauki. Operuje na środowisku Node serwując pliki za każdym razem, kiedy użytkownik wchodzi na dany adres. Dodatkowo, daje możliwość budowania wybranych przez nas stron również statycznie! Jest to idealne narzędzie wszędzie tam, gdzie nie sprawdzą się jedynie statyczne pliki, ani kod generowany w locie.

Wady i zalety SSR

Zalety:

  • dobra wydajność
  • dobre wsparcie dla SEO, ale nie tak dobre jak przy pełnym SSG
  • możliwość ukrycia wrażliwych danych w pliku .env
  • pełnoprawny backend, który w niczym nas nie ogranicza

Wady:

  • wiele nowych rzeczy do nauki
  • trudność w hostowaniu - prawdopodobnie konieczność użycia Dockera (w małych projektach można skorzystać z darmowego Vercela)
  • mieszanie środowisk klienta oraz backendu w jednym środowisku, co może sprawiać małe problemy

Kiedy warto używać:

  • duże aplikacje, z masą danych to wyświetlania jak i aktualizowania,
  • aplikacje do zarządzania (np. z loginem), i jednocześnie wymagające SEO (np. e-commerce)

Podsumowanie

zdjęcie tematyczne
porównanie sposobów renderowania stron internetowych oraz frameworków

To, co wybierzemy do serwowania naszej aplikacji w sieci będzie zależało od wielu aspektów. Jedno narzędzie będzie robić coś lepiej aniżeli drugie. Wszystko jest też oparte na tradeoffach, także wybieraj mądrze :)

© Damian Kalka 2022
Wszelkie prawa zastrzeżone