Jedną z największych zalet React.js ma być renderowanie po stronie serwera . Problem polega na tym, że funkcja klucza React.renderComponentToString()
jest synchroniczna, co uniemożliwia ładowanie jakichkolwiek danych asynchronicznych, gdy hierarchia komponentów jest renderowana na serwerze.
Powiedzmy, że mam uniwersalny komponent do komentowania, który mogę umieścić w dowolnym miejscu na stronie. Ma tylko jedną właściwość, jakiś identyfikator (np. Id artykułu, pod którym umieszczane są komentarze), a całą resztą zajmuje się sam komponent (ładowanie, dodawanie, zarządzanie komentarzami).
Bardzo podoba mi się architektura Flux, ponieważ znacznie ułatwia wiele rzeczy, a jej sklepy są idealne do udostępniania stanu między serwerem a klientem. Po zainicjowaniu mojego sklepu zawierającego komentarze mogę go po prostu serializować i wysłać z serwera do klienta, gdzie można go łatwo przywrócić.
Pytanie brzmi, jak najlepiej zapełnić mój sklep. W ciągu ostatnich dni dużo googlowałem i natknąłem się na kilka strategii, z których żadna nie wydawała się naprawdę dobra, biorąc pod uwagę, jak bardzo ta funkcja React jest „promowana”.
Moim zdaniem najprostszym sposobem jest wypełnienie wszystkich moich sklepów przed rozpoczęciem właściwego renderowania. Oznacza to, że znajduje się poza hierarchią komponentów (na przykład podłączony do mojego routera). Problem z tym podejściem polega na tym, że musiałbym prawie dwukrotnie zdefiniować strukturę strony. Rozważ bardziej złożoną stronę, na przykład stronę bloga z wieloma różnymi składnikami (rzeczywisty post na blogu, komentarze, pokrewne posty, najnowsze posty, strumień na Twitterze ...). Musiałbym zaprojektować strukturę strony za pomocą komponentów Reacta, a następnie gdzie indziej musiałbym zdefiniować proces zapełniania każdego wymaganego sklepu dla tej bieżącej strony. Nie wydaje mi się to miłym rozwiązaniem. Niestety większość samouczków izomorficznych jest zaprojektowana w ten sposób (na przykład ten wspaniały samouczek dotyczący flux ).
React-async . To podejście jest doskonałe. Pozwala mi to po prostu zdefiniować w specjalnej funkcji w każdym komponencie, jak zainicjować stan (nie ma znaczenia, czy synchronicznie czy asynchronicznie), a te funkcje są wywoływane, gdy hierarchia jest renderowana do HTML. Działa w taki sposób, że składnik nie jest renderowany, dopóki stan nie zostanie całkowicie zainicjowany. Problem w tym, że wymaga włókienczyli, o ile rozumiem, rozszerzenie Node.js, które zmienia standardowe zachowanie JavaScript. Chociaż bardzo mi się podoba wynik, nadal wydaje mi się, że zamiast znaleźć rozwiązanie, zmieniliśmy zasady gry. I myślę, że nie powinniśmy być do tego zmuszani, aby korzystać z tej podstawowej funkcji React.js. Nie jestem też pewien ogólnego wsparcia tego rozwiązania. Czy można używać Fibre na standardowym serwerze WWW Node.js?
Myślałem trochę sam. Tak naprawdę nie zastanawiałem się nad szczegółami implementacji, ale ogólna idea jest taka, że powinienem rozszerzyć komponenty w podobny sposób jak React-async, a następnie wielokrotnie wywoływać React.renderComponentToString () na komponencie głównym. Podczas każdego przejazdu zbierałem przedłużające się callbacki, a następnie dzwoniłem do nich na końcu przepustki, aby zapełnić sklepy. Powtarzałbym ten krok, aż wszystkie magazyny wymagane przez aktualną hierarchię komponentów zostałyby wypełnione. Jest wiele rzeczy do rozwiązania i jestem szczególnie niepewny co do wydajności.
Przegapiłem coś? Czy jest inne podejście / rozwiązanie? W tej chwili myślę o przejściu na drogę reagowania asynchronicznego / włókien, ale nie jestem tego do końca pewien, jak wyjaśniono w drugim punkcie.
Powiązana dyskusja na GitHub . Najwyraźniej nie ma oficjalnego podejścia ani nawet rozwiązania. Może prawdziwe pytanie brzmi, jak mają być używane komponenty React. Na przykład prosta warstwa widoku (w zasadzie moja sugestia numer jeden) czy prawdziwie niezależne i samodzielne komponenty?
źródło
Odpowiedzi:
Jeśli używaszreak-router , możesz po prostu zdefiniować
willTransitionTo
metody w komponentach, które otrzymająTransition
obiekt, który możesz wywołać.wait
.Nie ma znaczenia, czy funkcja renderToString jest synchroniczna, ponieważ wywołanie zwrotne do
Router.run
nie zostanie wywołane, dopóki wszystkie.wait
obietnice ed nie zostaną rozwiązane, więc do czasurenderToString
wywołania w oprogramowaniu pośredniczącym można było zapełnić sklepy. Nawet jeśli sklepy są singletonami, możesz po prostu tymczasowo ustawić ich dane dokładnie na czas przed wywołaniem renderowania synchronicznego, a komponent je zobaczy.Przykład oprogramowania pośredniego:
routes
Przedmiot (który opisuje hierarchię tras) jest dzielona verbatim z klientem i serweremźródło
willTransitionTo
metodę. Co oznacza, że nadal nie jest możliwe napisanie całkowicie samodzielnych komponentów wielokrotnego użytku, takich jak ten, który opisałem w pytaniu. Ale jeśli nie chcemy iść na Fibers, jest to prawdopodobnie najlepszy i najbardziej reagujący sposób na zaimplementowanie renderowania po stronie serwera.transition
obiekt jako parametr, więc po prostu zadzwonisztransition.wait(yourPromise)
. To oczywiście oznacza, że musisz zaimplementować swoje API, aby obsługiwać obietnice. Inną wadą tego podejścia jest to, że nie ma prostego sposobu na zaimplementowanie „wskaźnika obciążenia” po stronie klienta. Przejście nie przełączy się na komponent obsługi trasy, dopóki wszystkie obietnice nie zostaną rozwiązane..waited
związane z przejściem. Gdy wszystkie z nich zostaną spełnione,.run
wywoływane jest wywołanie zwrotne. Tuż przed.render()
zebraniem wszystkich danych z obietnic i ustawieniem stanów magazynu singelton, w następnym wierszu po wywołaniu renderowania inicjalizuję z powrotem sklepy singleton. To dość hakerskie, ale wszystko dzieje się automatycznie, a kod komponentu i aplikacji pozostaje praktycznie taki sam.Wiem, że prawdopodobnie nie jest to dokładnie to, czego chcesz i może to nie mieć sensu, ale pamiętam, jak radziłem sobie z niewielką modyfikacją komponentu, aby obsłużyć oba:
Więc coś takiego:
Przepraszam, że nie mam pod ręką dokładnego kodu, więc może to nie zadziałać po wyjęciu z pudełka, ale piszę w celu dyskusji.
Znowu chodzi o to, aby leczyć większość składnika jako niemy widzenia, oraz radzenia sobie z pobierania danych jak najwięcej out składnika.
źródło
<script type=application/json>{initState}</script>
; w ten sposób dane będą w HTML. Ponownie nawadniaj / powiąż zdarzenia interfejsu użytkownika ze stroną, wywołując renderowanie na kliencie. Kolejne strony są tworzone przez kod js klienta (pobieranie danych w razie potrzeby) i renderowane przez klienta. W ten sposób każde odświeżenie załaduje nowe strony SSR, a kliknięcie strony będzie CSR. = izomorficzny i przyjazny dla SEONaprawdę się tym dzisiaj bawiłem i chociaż nie jest to odpowiedź na twój problem, zastosowałem to podejście. Chciałem używać Express do routingu, a nie React Router, i nie chciałem używać Fibers, ponieważ nie potrzebowałem obsługi wątków w węźle.
Dlatego właśnie podjąłem decyzję, że dla danych początkowych, które muszą być przekazane do magazynu flux podczas ładowania, wykonam żądanie AJAX i przekażę dane początkowe do magazynu
W tym przykładzie używałem Fluxxor.
A więc na mojej trasie ekspresowej, w tym przypadku
/products
trasa:Następnie moja metoda renderowania inicjalizacji, która przekazuje dane do sklepu.
źródło
Wiem, że to pytanie zostało zadane rok temu, ale mieliśmy ten sam problem i rozwiązujemy go za pomocą zagnieżdżonych obietnic, które pochodzą z komponentów, które mają być renderowane. W końcu mieliśmy wszystkie dane dotyczące aplikacji i po prostu wysłaliśmy je.
Na przykład:
iw routerze
źródło
Chcę podzielić się z wami moim podejściem do renderowania po stronie serwera
Flux
, trochę uproszczone, na przykład:Powiedzmy, że mamy
component
wstępne dane ze sklepu:Jeśli klasa wymaga pewnych wstępnie załadowanych danych dla stanu początkowego, stwórzmy Loader dla
MyComponent
:Sklep:
Teraz po prostu załaduj dane do routera:
źródło
tak jak krótki pakiet -> GraphQL rozwiąże to całkowicie dla twojego stosu ...
-> getDataFromTree automatycznie znajdzie wszystkie zaangażowane zapytania w Twojej aplikacji i wykona je, umieszczając pamięć podręczną Apollo na serwerze, umożliwiając w ten sposób w pełni działające SSR. BĘM
źródło