Einer der größten Vorteile von React.js soll das serverseitige Rendern sein . Das Problem ist, dass die Schlüsselfunktion React.renderComponentToString()
synchron ist, wodurch es unmöglich wird, asynchrone Daten zu laden, wenn die Komponentenhierarchie auf dem Server gerendert wird.
Angenommen, ich habe eine universelle Komponente zum Kommentieren, die ich praktisch überall auf der Seite ablegen kann. Es hat nur eine Eigenschaft, eine Art Bezeichner (z. B. die ID eines Artikels, unter dem die Kommentare platziert sind), und alles andere wird von der Komponente selbst verwaltet (Laden, Hinzufügen, Verwalten von Kommentaren).
Ich mag die Flux- Architektur sehr, weil sie viele Dinge viel einfacher macht und ihre Geschäfte perfekt sind, um den Status zwischen Server und Client zu teilen. Sobald mein Geschäft mit Kommentaren initialisiert ist, kann ich es einfach serialisieren und vom Server an den Client senden, wo es leicht wiederhergestellt werden kann.
Die Frage ist, wie ich meinen Laden am besten bevölkern kann. In den letzten Tagen habe ich viel gegoogelt und bin auf einige Strategien gestoßen, von denen keine wirklich gut schien, wenn man bedenkt, wie sehr diese Funktion von React "beworben" wird.
Meiner Meinung nach ist es am einfachsten, alle meine Geschäfte zu füllen, bevor das eigentliche Rendern beginnt. Das heißt, irgendwo außerhalb der Komponentenhierarchie (zum Beispiel an meinen Router angeschlossen). Das Problem bei diesem Ansatz ist, dass ich die Seitenstruktur ziemlich zweimal definieren müsste. Stellen Sie sich eine komplexere Seite vor, beispielsweise eine Blog-Seite mit vielen verschiedenen Komponenten (tatsächlicher Blog-Beitrag, Kommentare, verwandte Beiträge, neueste Beiträge, Twitter-Stream ...). Ich müsste die Seitenstruktur mithilfe von React-Komponenten entwerfen und dann an einer anderen Stelle den Prozess des Auffüllens jedes erforderlichen Speichers für diese aktuelle Seite definieren. Das scheint mir keine gute Lösung zu sein. Leider sind die meisten isomorphen Tutorials so konzipiert (zum Beispiel dieses großartige Flux-Tutorial ).
Reagieren Sie asynchron . Dieser Ansatz ist perfekt. Ich kann einfach in einer speziellen Funktion in jeder Komponente definieren, wie der Status initialisiert werden soll (egal ob synchron oder asynchron), und diese Funktionen werden aufgerufen, wenn die Hierarchie in HTML gerendert wird. Es funktioniert so, dass eine Komponente erst gerendert wird, wenn der Status vollständig initialisiert ist. Das Problem ist, dass Fasern erforderlich sindSoweit ich weiß, handelt es sich um eine Node.js-Erweiterung, die das Standardverhalten von JavaScript ändert. Obwohl mir das Ergebnis wirklich gefällt, scheint es mir immer noch, dass wir, anstatt eine Lösung zu finden, die Spielregeln geändert haben. Und ich denke, wir sollten nicht dazu gezwungen werden, um diese Kernfunktion von React.js zu nutzen. Ich bin mir auch nicht sicher über die allgemeine Unterstützung dieser Lösung. Ist es möglich, Fibre auf Standard-Webhosting von Node.j zu verwenden?
Ich habe ein wenig alleine nachgedacht. Ich habe nicht wirklich über die Implementierungsdetails nachgedacht, aber die allgemeine Idee ist, dass ich die Komponenten ähnlich wie React-async erweitern und dann wiederholt React.renderComponentToString () für die Stammkomponente aufrufen würde. Während jedes Durchgangs sammelte ich die erweiterten Rückrufe und rief sie dann am und des Durchgangs an, um die Geschäfte zu füllen. Ich würde diesen Schritt wiederholen, bis alle für die aktuelle Komponentenhierarchie erforderlichen Speicher gefüllt sind. Es gibt viele Dinge zu lösen und ich bin mir über die Leistung besonders unsicher.
Habe ich etwas verpasst? Gibt es einen anderen Ansatz / eine andere Lösung? Im Moment denke ich darüber nach, asynchron zu reagieren / Fasern zu gehen, bin mir aber nicht ganz sicher, wie im zweiten Punkt erläutert.
Verwandte Diskussion auf GitHub . Anscheinend gibt es keinen offiziellen Ansatz oder gar eine Lösung. Vielleicht ist die eigentliche Frage, wie die React-Komponenten verwendet werden sollen. Wie eine einfache Ansichtsebene (so ziemlich mein Vorschlag Nummer eins) oder wie echte unabhängige und eigenständige Komponenten?
quelle
Antworten:
Wenn du benutzt React-Router verwenden , können Sie einfach eine
willTransitionTo
Methode in Komponenten definieren, die einTransition
Objekt übergibt, das Sie aufrufen können.wait
.Es spielt keine Rolle, ob renderToString synchron ist, da der Rückruf an
Router.run
erst aufgerufen wird, wenn alle.wait
Ed-Versprechen gelöst sind. Wenn also die ZeitrenderToString
in der Middleware aufgerufen wird, könnten Sie die Stores gefüllt haben. Selbst wenn es sich bei den Speichern um Singletons handelt, können Sie ihre Daten nur vorübergehend just-in-time festlegen, bevor der synchrone Rendering-Aufruf und die Komponente sie sehen.Beispiel für Middleware:
Das
routes
Objekt (das die Routenhierarchie beschreibt) wird wörtlich mit Client und Server geteiltquelle
willTransitionTo
Methode unterstützen. Dies bedeutet, dass es immer noch nicht möglich ist, vollständig eigenständige wiederverwendbare Komponenten wie die in der Frage beschriebene zu schreiben. Aber wenn wir nicht bereit sind, uns für Glasfasern zu entscheiden, ist dies wahrscheinlich die beste und reaktionsschnellste Methode, um serverseitiges Rendering zu implementieren.transition
Objekt als Parameter und rufen es einfach auftransition.wait(yourPromise)
. Das bedeutet natürlich, dass Sie Ihre API implementieren müssen, um Versprechen zu unterstützen. Ein weiterer Nachteil dieses Ansatzes besteht darin, dass es keine einfache Möglichkeit gibt, einen "Ladeindikator" auf der Clientseite zu implementieren. Der Übergang wechselt erst zur Route-Handler-Komponente, wenn alle Versprechen erfüllt sind..waited
für einen Übergang sammelt . Sobald alle erfüllt sind, wird der.run
Rückruf aufgerufen. Kurz bevor.render()
ich alle Daten aus den Versprechungen zusammen sammle und die Singelton-Speicherzustände festlege, initialisiere ich in der nächsten Zeile nach dem Render-Aufruf die Singleton-Speicher zurück. Es ist ziemlich hackig, aber alles geschieht automatisch und der Code der Komponenten- und Speicheranwendung bleibt praktisch gleich.Ich weiß, dass dies wahrscheinlich nicht genau das ist, was Sie wollen, und es macht möglicherweise keinen Sinn, aber ich erinnere mich, dass ich die Komponente leicht modifiziert habe, um beides zu handhaben:
Also so etwas wie:
Es tut mir leid, dass ich nicht den genauen Code zur Hand habe, daher funktioniert dies möglicherweise nicht sofort, aber ich poste im Interesse der Diskussion.
Auch hier besteht die Idee darin, den größten Teil der Komponente als dumme Ansicht zu behandeln und so viel Daten wie möglich aus der Komponente abzurufen.
quelle
<script type=application/json>{initState}</script>
. Auf diese Weise werden die Daten im HTML-Code gespeichert. Rehydrieren / binden Sie UI-Ereignisse an die Seite, indem Sie render auf dem Client aufrufen. Nachfolgende Seiten werden vom js-Code des Clients erstellt (Daten nach Bedarf abrufen) und vom Client gerendert. Auf diese Weise lädt jede Aktualisierung neue SSR-Seiten und das Klicken auf eine Seite ist CSR. = isomorph & SEO freundlichIch war heute wirklich durcheinander und obwohl dies keine Antwort auf Ihr Problem ist, habe ich diesen Ansatz verwendet. Ich wollte Express eher für das Routing als für React Router verwenden, und ich wollte keine Glasfasern verwenden, da ich keine Threading-Unterstützung im Knoten benötigte.
Daher habe ich gerade entschieden, dass ich für Anfangsdaten, die beim Laden in den Flussspeicher gerendert werden müssen, eine AJAX-Anforderung ausführen und die Anfangsdaten an den Speicher übergeben werde
Ich habe Fluxxor für dieses Beispiel verwendet.
Also auf meiner Expressroute, in diesem Fall eine
/products
Route:Dann meine Rendermethode initialisieren, die die Daten an den Speicher weitergibt.
quelle
Ich weiß, dass diese Frage vor einem Jahr gestellt wurde, aber wir hatten das gleiche Problem und lösen es mit verschachtelten Versprechungen, die von den Komponenten abgeleitet wurden, die gerendert werden sollen. Am Ende hatten wir alle Daten für die App und haben sie einfach nach unten geschickt.
Beispielsweise:
und im Router
quelle
Möchten Sie meinen Ansatz des serverseitigen Renderns mit Ihnen teilen, lassen Sie
Flux
sich zum Beispiel wenig vereinfachen:Nehmen wir an, wir haben
component
mit anfänglichen Daten aus dem Speicher:Wenn für die Klasse einige vorinstallierte Daten für den Anfangszustand erforderlich sind, erstellen wir einen Loader für
MyComponent
:Geschäft:
Laden Sie jetzt einfach die Daten in den Router:
quelle
Nur als kurzes Rollup -> GraphQL löst dies vollständig für Ihren Stack ...
-> getDataFromTree findet automatisch alle beteiligten Abfragen in Ihrer App und führt sie aus, indem es Ihren Apollo-Cache auf dem Server poupliert und so SSR .. BÄM voll funktionsfähig macht
quelle