L'un des plus grands avantages de React.js est censé être le rendu côté serveur . Le problème est que la fonction clé React.renderComponentToString()
est synchrone, ce qui rend impossible le chargement de données asynchrones lorsque la hiérarchie des composants est rendue sur le serveur.
Disons que j'ai un composant universel pour les commentaires que je peux déposer à peu près n'importe où sur la page. Il n'a qu'une seule propriété, une sorte d'identifiant (par exemple l'id d'un article sous lequel les commentaires sont placés), et tout le reste est géré par le composant lui-même (chargement, ajout, gestion des commentaires).
J'aime beaucoup l' architecture Flux car elle facilite beaucoup de choses et ses magasins sont parfaits pour partager l'état entre le serveur et le client. Une fois que mon magasin contenant des commentaires est initialisé, je peux simplement le sérialiser et l'envoyer du serveur au client où il est facilement restauré.
La question est de savoir quelle est la meilleure façon de peupler mon magasin. Au cours des derniers jours, j'ai beaucoup cherché sur Google et je suis tombé sur quelques stratégies, dont aucune ne me paraissait vraiment bonne compte tenu de la "promotion" de cette fonctionnalité de React.
À mon avis, le moyen le plus simple est de peupler tous mes magasins avant le début du rendu réel. Cela signifie quelque part en dehors de la hiérarchie des composants (accroché à mon routeur par exemple). Le problème avec cette approche est que je devrais définir à peu près deux fois la structure de la page. Prenons une page plus complexe, par exemple une page de blog avec de nombreux composants différents (article de blog réel, commentaires, articles associés, articles les plus récents, flux Twitter ...). Je devrais concevoir la structure de la page à l'aide des composants React, puis ailleurs, je devrais définir le processus de remplissage de chaque magasin requis pour cette page actuelle. Cela ne me semble pas être une bonne solution. Malheureusement, la plupart des didacticiels isomorphes sont conçus de cette façon (par exemple ce grand didacticiel sur les flux ).
Réagissez-async . Cette approche est parfaite. Cela me permet simplement de définir dans une fonction spéciale dans chaque composant comment initialiser l'état (peu importe que ce soit de manière synchrone ou asynchrone) et ces fonctions sont appelées lorsque la hiérarchie est rendue en HTML. Cela fonctionne de telle sorte qu'un composant n'est pas rendu tant que l'état n'est pas complètement initialisé. Le problème est qu'il nécessite des fibresqui est, pour autant que je sache, une extension Node.js qui modifie le comportement JavaScript standard. Bien que j'aime beaucoup le résultat, il me semble toujours qu'au lieu de trouver une solution, nous avons changé les règles du jeu. Et je pense que nous ne devrions pas être obligés de faire cela pour utiliser cette fonctionnalité de base de React.js. Je ne suis pas non plus sûr du support général de cette solution. Est-il possible d'utiliser la fibre sur l'hébergement Web standard Node.js?
Je réfléchissais un peu par moi-même. Je n'ai pas vraiment réfléchi aux détails de l'implémentation, mais l'idée générale est que j'étendrais les composants de la même manière que React-async, puis j'appellerais à plusieurs reprises React.renderComponentToString () sur le composant racine. Au cours de chaque passage, je collectais les rappels étendus, puis je les appelais au et du passage pour peupler les magasins. Je répéterais cette étape jusqu'à ce que tous les magasins requis par la hiérarchie actuelle des composants soient remplis. Il y a beaucoup de choses à résoudre et je ne suis particulièrement pas sûr de la performance.
Ai-je oublié quelque chose? Existe-t-il une autre approche / solution? En ce moment, je pense aller dans le sens de la réaction asynchrone / fibres mais je n'en suis pas complètement sûr, comme expliqué dans le deuxième point.
Discussion connexe sur GitHub . Apparemment, il n'y a pas d'approche officielle ni même de solution. Peut-être que la vraie question est de savoir comment les composants React sont destinés à être utilisés. Comme une simple couche de vue (à peu près ma suggestion numéro un) ou comme de vrais composants indépendants et autonomes?
la source
Réponses:
Si vous utilisez react-router , vous pouvez simplement définir une
willTransitionTo
méthode dans les composants, qui reçoit unTransition
objet que vous pouvez appeler.wait
.Peu importe si renderToString est synchrone, car le rappel vers
Router.run
ne sera pas appelé tant que toutes les.wait
promesses ed ne seront pas résolues, donc au moment où ilrenderToString
sera appelé dans le middleware, vous auriez pu remplir les magasins. Même si les magasins sont des singletons, vous pouvez simplement définir leurs données temporairement juste à temps avant l'appel de rendu synchrone et le composant le verra.Exemple de middleware:
L'
routes
objet (qui décrit la hiérarchie des routes) est partagé textuellement avec le client et le serveurla source
willTransitionTo
méthode. Ce qui signifie qu'il n'est toujours pas possible d'écrire des composants réutilisables complètement autonomes comme celui que j'ai décrit dans la question. Mais à moins que nous ne soyons disposés à utiliser Fibers, c'est probablement le moyen le meilleur et le plus réactif pour implémenter le rendu côté serveur.transition
objet en tant que paramètre, vous appellerez donc simplementtransition.wait(yourPromise)
. Cela signifie bien sûr que vous devez implémenter votre API pour tenir les promesses. Un autre inconvénient de cette approche est qu'il n'y a pas de moyen simple d'implémenter un "indicateur de chargement" côté client. La transition ne passera pas au composant de gestionnaire d'itinéraire tant que toutes les promesses ne seront pas résolues..waited
pour une transition. Une fois que tous sont remplis, le.run
rappel est appelé. Juste avant de.render()
collecter toutes les données des promesses et de définir les états du magasin singleton, puis sur la ligne suivante après l'appel de rendu, je réinitialise les magasins singleton. C'est assez piraté mais tout se passe automatiquement et le code du composant et de l'application de magasin reste pratiquement le même.Je sais que ce n'est probablement pas exactement ce que vous voulez, et cela n'a peut-être pas de sens, mais je me souviens de m'être débrouillé en modifiant légèrement le composant pour gérer les deux:
Donc quelque chose comme:
Je suis désolé de ne pas avoir le code exact sous la main, donc cela pourrait ne pas fonctionner immédiatement, mais je publie dans l'intérêt de la discussion.
Encore une fois, l'idée est de traiter la plupart des composants comme une vue stupide, et de traiter l' extraction de données, autant que possible hors du composant.
la source
<script type=application/json>{initState}</script>
; de cette façon, les données seront dans le HTML. Réhydratez / liez les événements de l'interface utilisateur à la page en appelant render sur le client. Les pages suivantes sont créées par le code js du client (récupérant les données selon les besoins) et rendues par le client. De cette façon, toute actualisation chargera de nouvelles pages SSR et cliquer sur une page sera CSR. = isomorphe & SEO friendlyJ'ai été vraiment dérangé avec cela aujourd'hui, et bien que ce ne soit pas une réponse à votre problème, j'ai utilisé cette approche. Je voulais utiliser Express pour le routage plutôt que React Router, et je ne voulais pas utiliser Fibers car je n'avais pas besoin de support de threading dans le nœud.
Je viens donc de décider que pour les données initiales qui doivent être rendues au magasin de flux lors du chargement, je vais effectuer une requête AJAX et transmettre les données initiales au magasin
J'utilisais Fluxxor pour cet exemple.
Donc sur mon itinéraire express, dans ce cas un
/products
itinéraire:Ensuite, ma méthode de rendu initialize qui transmet les données au magasin.
la source
Je sais que cette question a été posée il y a un an mais nous avons eu le même problème et nous le résolvons avec des promesses imbriquées dérivées des composants qui vont être rendus. En fin de compte, nous avons eu toutes les données de l'application et nous l'avons simplement envoyée.
Par exemple:
et dans le routeur
la source
Je veux partager avec vous mon approche du rendu côté serveur en utilisant
Flux
, peu être simplifié par exemple:Disons que nous avons
component
avec les données initiales du magasin:Si la classe nécessite des données préchargées pour l'état initial, créons Loader pour
MyComponent
:Boutique:
Maintenant, chargez simplement les données dans le routeur:
la source
tout comme un bref rollup -> GraphQL résoudra cela entièrement pour votre pile ...
-> getDataFromTree trouvera automatiquement toutes les requêtes impliquées dans votre application et les exécutera, plaçant votre cache apollo sur le serveur et activant ainsi le SSR pleinement opérationnel. BÄM
la source