Inspirację do napisania tego wpisu, jak to często już u mnie bywało, przyniosło samo życie. Jednym z pierwszych projektów w Going., w którym przyszło mi brać udział było stworzenie statycznej strony strony www. Mogłem oczywiście napisać wszystko w czystym HTML + CSS + jQuery (sic…). Strona miała jednak zawierać dość dużo logiki biznesowej w JavaScript, do tego routing, a poza tym chodziło również o to, aby rozwiązanie było podstawą do dalszej rozbudowy w przyszłości.

Zdecydowałem więc, że trzeba by to jednak jakoś ogarnąć w React… Idealnie byłoby oprzeć całość na Create React App bez “ejectowania” go, tak aby w przyszłości mieć mniej problemów z podbijaniem wersji pakietów. Po krótkim research’u okazało się, że to co chcę osiągnąć (generowanie statycznych podstron na podstawie routingu zdefiniowanego w aplikacji React) jest jak najbardziej możliwe. O szczegółach tego rozwiązania przeczytasz w niniejszym wpisie.

Biblioteka react-snapshot

Jak już nie raz i nie dwa się przekonałem, w repozytoriach npm są rozwiązania prawie na wszystkie problemy… Nie inaczej było także w tym przypadku. Okazuje się, że istnieje biblioteka, która robi dokładnie to czego potrzebuję! Mowa tutaj o react-snapshot.

Sposób użycia tej biblioteki w aplikacji opartej o Create React App jest banalnie prosty! Oczywiście pierwsza rzecz to instalacja biblioteki w naszym projekcie:

yarn add --dev react-snapshot

Kolejna rzecz to drobna modyfikacja skryptu npm, służącego do budowania aplikacji (nie potrzebujemy snapshotów w trybie developerskim):

"build": "react-scripts build && react-snapshot"

Powyższe, po zakończeniu budowania aplikacji, uruchamia generowanie statycznych stron HTML na podstawie podstron istniejących w routingu naszej strony.

Ostatnia rzecz do zrobienia, to lekka zmiana w pliku index.js:

// import ReactDOM from 'react-dom';
import { render } from 'react-snapshot';

import App from './App';

render(
  <App/>,
  document.getElementById('root')
);

Jak widzisz, biblioteka react-snapshot dostarcza własną wersję metody render. Aby więc generowanie statycznych podstron działało, wystarczy zaimportować ją i użyć zamiast standardowej metody render, którą zwykle importujemy z pakietu react-dom.

I to w zasadzie wszystko. Wystarczy teraz uruchomić polecenie

yarn build

…a po chwili w katalogu build projektu pojawią się pliki HTML odpowiadające poszczególnym podstronom naszej aplikacji.

W tym miejscu warto zwrócić uwagę, w jaki sposób tworzone są katalogi i pliki w oparciu o routing naszej aplikacji. Np. dla ścieżki /costam-costam/123 utworzony zostanie katalog costam-costam, a w nim znajdzie się plik HTML o nazwie 123.html. Warto mieć to na uwadze przy konfiguracji serwera.

Dodatkowa konfiguracja

Korzystając z biblioteki react-snapshot dość szybko możemy natknąć się na pewien problem: struktura drzewa stron naszej aplikacji budowana jest w oparciu o linki dostępne w aplikacji. Jeśli więc do którejś z podstron nie ma w aplikacji odnośnika, react-snapshot może nie wygenerować dla niej pliku HTML.

Na szczęście można sobie z tym dość łatwo poradzić. Wystarczy do pliku package.json dodać sekcję react-snapshot:

"reactSnapshot": {
  "include": [
    "/not-found",
  ],
  "exclude": [
    "/signup",
  ],
  "snapshotDelay": 300
}

W powyższym przykładzie informuję bibliotekę react-snapshot o konieczności odwiedzenia ścieżki /not-found podczas generowania statycznych stron. Nastąpi to nawet jeśli w aplikacji nie ma ani jednego odnośnika do tej strony.

Oprócz tego, wyłączam z generowania ścieżkę /signup - może się to przydać jeśli nie chcemy by narzędzie generowało plik HTML dla jakichś obszarów aplikacji.

Standardowo, biblioteka czeka 50ms na wyrenderowanie strony zanim wygeneruje na tej podstawie statyczną stronę. Ostatni parametr, snapshotDelay pozwala zmienić opóźnienie generowania snapshotów tak by dopasować je do potrzeb naszej aplikacji.

Zastosowania i podsumowanie

Jak na pewno się domyślasz, tego typu biblioteka nie nadaje się do wszystkiego… Jej podstawowym założeniem jest, że wszystkie dane wyświetlane na stronie są statyczne. Nie jest więc możliwe wykorzystanie tej biblioteki w przypadku generowania linków do podstron na podstawie danych pobieranych asynchronicznie z API!

Jest to natomiast świetne rozwiązanie do wszelkiego rodzaju stron typu landing-page, nawet mocno rozbudowanych, zawierających wiele statycznych podstron. W przypadku tego typu aplikacji myślę, że biblioteka react-snapshot może okazać się przydatna.