Aplikacje renderowane po stornie serwera są z nami od wielu lat. Cała koncepcja opiera się na tym, że to serwer wysyła do naszej przeglądarki gotowy dokument HTML, a nasza maszyna nie musi się zbytnio wysilać, aby daną teść nam zaprezentować.
Wraz ze wzrostem popularności wszelakich bibliotek JavaScript, zaczęły pojawiać się aplikacje typu SPA (Single Page Application), które odwróciły ten proces. Teraz serwer zwracał niezbędny kod JS, a generowanie HTML’a odbywa się w przeglądarce klienta. Aplikacje taką można też określić jako CSR (Client Side Rendered). Każde z tych rozwiązań ma swoje wady i zalety.
Server-side rendering, zalety:
- Mniejsze obciążenie maszyny klienta
- Świetny dla SEO
- Świetny dla social media sharing
Server-side rendering, wady:
- Duże obciążenie serwera
- Przeładowania całej strony podczas nawigacji
- Generowania dokumentów HTML przez serwer może wydłużyć TTFB (Time to first byte)
Client-side rendering, zalety:
- Mniejsze obciążenie serwera
- Szybsza nawigacja po wczytaniu aplikacji
- Doskonały dla aplikacji webowych
Client-side rendering, wady:
- Długi czas oczekiwanie na załadowanie się aplikacji
- Może powodować problemy SEO
- Może powodować problemy z wydajnością, zwłaszcza na starszych urządzeniach mobilnych
Następny krok
Było tylko kwestią czasu, aż ktoś wpadnie na pomysł połączenia koncepcji SSR i CSR i tak też się stało. Narodził się tak zwany „Uniwersalny JavaScript”, oznaczający kod JS, który może być uruchomiony „wszędzie” (chodzi o uruchomienie tego samego kodu zarówno na serwerze, jak i w przeglądarce).
Takie podejście zapoczątkowało tworzenie rozwiązań, które umożliwią renderowanie aplikacji frontendowych po stronie serwera minimalizując, tym samym, wady aplikacji typu SPA/CSR.
SSR z Angular Universal
Dokumentacja definiuje Angular Universal jako technologię pozwalającą na renderowanie aplikacji Angularowych po stronie serwera. W poradniku opisany jest cały proces dodawania tej funkcjonalności. Jest on rzeczywiście prosty, bo wymaga wpisania jednej komendy (w przypadku projektu utworzonego przez angualr-cli), ale wiąże się on z kilkoma konsekwencjami, które niestety, nie są w jasny sposób opisane.
O czym więc na należy pamiętać?
- Aplikacja uruchamia się w dokładnie ten sam sposób, zarówno w przeglądarce, jak i na serwerze. Mając np. zdefiniowany APP_INITIALIZER będzie się uruchamiał 2 razy, co nie zawsze będzie pożądanym efektem.
- Po stronie serwera nie mamy dostępu do obiektu window, document itp.
- Bez dodatkowych modułów lub instrukcji warunkowych każde zapytanie do api zostanie wykonane również po stornie serwera, co może powodować długie oczekiwanie na dokument HTML.
- Nie należy modyfikować elementów DOM przez nativeElement.
- Nie należy używać metody setTimeout, setInterval.
Na szczęcie mamy dostęp do dwóch metod, które okazują się niezwykle przydatne podczas pracy z Angular Universal. Chodzi o isPlatformBrowser i isPlatformServer, które przyjmują parametr platformId i zwracają nam informację o tym, czy kod jest wykonywany po stronie serwera czy po stronie przeglądarki.
Jeżeli naprawę musimy skorzystać z któregoś z obiektów udostępnianych przez przeglądarkę np. document, możemy użyć jednej, z wyżej wymienionych, funkcji i wykonać fragment kodu warunkowo lub skorzystać z biblioteki domino, która utworzy DOM po stronie serwera.
Aby uniknąć podwójnych pytań do api możemy skorzystać z modułu TransferHttpCacheModule. Moduł ten rejestruje interceptor po stronie serwera, który przechwytuje wszystkie odpowiedzi api i tworzy obiekt stanu. Następnie jest przekazywany do przeglądarki zapobiegając w ten sposób ponownemu odpytaniu serwera o dane, które już mamy. Należy jednak pamiętać, że każde zapytanie po stronie serwera wydłuża TTFB, ponieważ aplikacja musi „zaczekać” na wszystkie dane, dopiero później może wygenerować dokument HTML.
Należ rownież pamiętać o włączeniu kompresji po stronie serwera, może ona w znaczący sposób wpłynąć na rozmiar przesyłanych danych.
Co nowego w wersji 9?
Wraz z v9 programiści otrzymali do swojej dyspozycji wiele długo oczekiwanych usprawnień, takich jak statyczny pre-rendering, nowe buldery oraz co najważniejsze developerski tryb SSR (tak, do tej pory SSR był dostępny tylko jako produkcyjny build, co było niezmiernie irytujące i zarazem czasochłonne).
Aktualnie mamy do dyspozycji 2 tryby renderowania:
- Dynamic SSR - polegający na reperowaniu stron w „czasie rzeczywistym”.
- Static Prerendering - pozwalający na generowanie statycznych stron na etapie builda.
Napewno warto sprawdzić obie metody i zastanowić się, która lepiej pasuje do twojego projektu.
Podsumowanie
Angualr Universal zdecydowanie jest ciekawą technologią, którą w pewnych sytuacjach warto wdrożyć. Jeżeli zależy Ci na szybkim wczytaniu aplikacji, optymalizacji pod kątem SEO oraz na miniaturach w social media, tak naprawdę nie masz wyjścia. Na szczęście w wersji 9 mamy sporo udogodnień ułatwiających pracę z tą technologią. Zdecydowanie zachęcam do dalszego zgłębiania tematu, bo to dopiero początek.
Na koniec warto wspomnieć, że podobne rozważania są również dostępne dla innych wiodących frameworków JS. Dla React mamy do dyspozycji np. Next.js czy Gatsby, a dla Vue Nuxt,js i Quassar Framework.
Autorem tekstu jest Łukasz Zięba.