Wróć do strony głównej
Angular

Ciemna strona server side renderingu cz. 1

Server side rendering staje się coraz bardziej popularny i powszechny. Co nam daje, jak działa i z czym się to je opisał doskonale Tomek tutaj. W teorii dokumentacji Angulara odpalimy jedną komendę, zwrócimy uwagę na kilka rzeczy i gotowe – w praktyce okazuję się to nie takie proste i łatwe. W tym artykule postaram się zajrzeć w rejony i problemy, z którymi część was może się spotkać, a których rozwiązanie nie jest aż takie trywialne i przy tym znalezienie odpowiedzi w internecie czasem nawet niemożliwe. 

W pierwszej części spojrzymy na problem obiektu window na etapie builda, zainteresujemy się guardami i blokowaniem dostępu do aplikacji po stronie serwera np. w przypadku braku informacji o autoryzacji. W kolejnych rzucimy okiem na problemy initial state, zobaczymy jak pozbyć się podwójnie ładowanych animacji, dowiemy się jak przekazywać informacje między aplikacją kliencką i serwerową oraz jak radzić sobie z CSSami sterowanymi z poziomu kodu np. przy okazji media query i zajrzymy do obiektu request. No to jazda!

“Przecież mam mocka window, dlaczego to nie działa” – problemy na etapie buildowania aplikacji.

Jak powszechnie wiadomo po stronie serwera nie mamy przeglądarki więc nie mamy np. obiektu window – oczywiste, choć nie zawsze. Zdarzają się takie problemy z bibliotekami, którym nie pomaga zmockowanie window np. Hammer.js czy animate-css-grid. 

Przykład:

Prosty serwis konfiguracyjny hammera:

 

No i co może się wysypać, przecież nie ma tutaj żadnego obiektu window. Odpalamy build aplikacji, a konsola na to:

 

 

Pierwszy pomysł – nie ma obiektu window, zapomniałem go zmockować. Sprawdzamy nasz server.ts – wszystko na swoim miejscu, domino skutecznie mockuje nasz window. Próżno szukać rozwiązań w google, które nie wymagają ingerencji choćby w webpacka, a nawet w kod źródłowy biblioteki. Można jednakowoż temu zaradzić:

 

 

To rozwiązanie  pozwala załadować potrzebne biblioteki na żądanie. W kodzie sprawdzamy czy nasz window jest dostępny i ładujemy bibliotekę. Niby proste, ale potrafi napsuć krwi. Zapraszam również do przeczytania artykułu, ktory opisuje, jak można wykorzystać to rozwiązanie do lazy loadingu komponentów oraz co daje użyty powyżej komentarz

 

“Skąd mam wiedzieć czy jestem zautoryzowany” – czyli guardy po stronie serwera i wykluczanie ścieżek z SSR:

Kolejny problem na który można natrafić to wykluczenie ścieżki z konieczności SSR. Przykład –  jakaś część aplikacji wymaga autoryzacji, więc nie da się tego zrobić po stronie serwera lub potrzebujemy jakiegoś obiektu przeglądarki w naszym komponencie np. storage’a. Rozwiązań tego problemu jest kilka, ale moim zdaniem żadne nie daje w 100% zadowalającego rezultatu.

Pierwszy sposób to wykorzystanie naszego serwera i w przypadku odwołań do określonych ścieżek zwracanie po prostu index.html np.:

W moim przypadku pomysł w ogóle się nie sprawdził, przede wszystkim dlatego, że wszystkie ścieżki w pliku index.html muszą być bezwzględne, stąd wymaga to od nas jakkolwiek modyfikacji ścieżek w tymże pliku lub kombinowanie z renderowaniem.

Drugi sposób to wystawienie dwóch aplikacji – tej serwowanej przez SSR i  drugiej  serwowanej w sposób tradycyjny, a co za tym idzie odpowiednie kierowanie ruchu. Pomysł również nad wyraz średni, jeżeli nie powiedzieć zły.

Jeżeli ktoś decyduje się na któreś z powyższych rozwiązań musi wziąć pod uwagę jeszcze jeden problem – co jeżeli pojawi się wymaganie tłumaczenia routingu? Wtedy dla każdej ścieżki będzie trzeba robić odpowiednie przekierowanie, pomnożone przez liczbę obsługiwanych tłumaczeń, co dodatkowo dyskredytuje oba pomysły.

Nieidealny, ale moim zdaniem najprostszy i chyba najmniej inwazyjny jest sposób trzeci. On również posiada jedną zasadniczą wadę o czym jeszcze wspomnę. Ale wracając do sedna – dlaczego nie przekierować użytkownika na stronę tymczasową, która po wyrenderowaniu aplikacji przekieruje nas w odpowiednie miejsce? Przykład:

  1. Tworzymy sobie tymczasową stronę / komponent, który zobaczy użytkownik niech to będzie SsrRedirectComponent:

     
  2. Następnie stwórzmy guarda, który zadziała tylko po stronie serwera i przekieruje nas na odpowiednio zdefiniowaną ścieżkę:

    App.-routing.module

3. Powyższy guard przekieruje wersję serwerową naszej aplikacji  do naszego komponentu tymczasowego, natomiast nie zmieni routingu, dzięki czemu po załadowaniu aplikacji po stronie przeglądarki użytkownik nie zostanie na naszej stronie tymczasowej, ale zostanie przekierowany na stronę docelową.

Na naszym komponencie tymczasowym można wyświetlić chociażby loader i użytkownik nie zorientuje się, że jest w czymś na kształt poczekalni, szczególnie że w pasku adresu przeglądarki zobaczy adres docelowy, a aplikacja w międzyczasie zrobi robotę i albo nas wpuści dalej, albo przekieruje na stronę logowania czy też wyświetli odpowiedni popup. No właśnie wyświetli, albo nie wyświetli, ale o tym w drugiej części artykułu.

Podsumowanie

Podsumujmy, co udało się nam się dziś dowiedzieć. Po pierwsze – jak poradzić sobie z problemem brakujących obiektów w bibliotekach trzecich na etapie buildowania aplikacji bez konieczności ingerowania w ich kod. Po drugie dowiedzieliśmy się jak można w prosty sposób wykluczać ścieżki z Server Side Renderingu czy też radzić sobie z autoryzacją. Co dalej? Zapraszam do przeczytania kolejnych wpisów już niedługo. 

O autorze

Kamil Puczka

Stroniący od CSSów fullstack developer w pełni odnajdujący się w świecie Microsoft i .NET. Raczej grzebiący w architekturze aplikacji, aniżeli jej wizualnej części. Z Angularem od zarania dziejów. Po godzinach gitarzysta death metalowy i fan ciężkiego brzmienia.

Zapisz się do naszego newslettera. Bądź na bieżąco z najnowszymi trendami, poradami, meetupami i stań się częścią społeczności Angulara w Polsce. Rynek pracy docenia członków społeczności.

2 komentarzy

  1. Pingback: Ciemna strona server side renderingu cz.2 - Angular.love

  2. Pingback: The dark side of server side rendering part 2 - Angular.love

Dodaj komentarz

Twój adres e-mail nie zostanie opublikowany. Wymagane pola są oznaczone *