Utiliser React dans une application multi-pages

122

J'ai joué avec React et jusqu'à présent, je l'aime vraiment. Je crée une application avec NodeJS et je souhaite utiliser React pour certains des composants interactifs de l'application. Je ne veux pas en faire une application sur une seule page.

Je n'ai encore rien trouvé sur le Web qui réponde aux questions suivantes:

Comment diviser ou regrouper mes composants React dans une application multi-pages?

Actuellement, tous mes composants sont dans un seul fichier, même si je ne les charge jamais dans certaines sections de l'application.

Jusqu'à présent, j'essaie d'utiliser des instructions conditionnelles pour rendre les composants en recherchant l'ID du conteneur où React sera rendu. Je ne suis pas sûr à 100% des meilleures pratiques avec React. Cela ressemble à quelque chose comme ça.

if(document.getElementById('a-compenent-in-page-1')) {
    React.render(
        <AnimalBox url="/api/birds" />,
        document.getElementById('a-compenent-in-page-1')
    );
}

if(document.getElementById('a-compenent-in-page-2')) {
    React.render(
        <AnimalBox url="/api/cats" />,
        document.getElementById('a-compenent-in-page-2')
    );
}

if(document.getElementById('a-compenent-in-page-3')) {
    React.render(
        <AnimalSearchBox url="/api/search/:term" />,
        document.getElementById('a-compenent-in-page-3')
    );
}

Je lis toujours la documentation et je n'ai pas encore trouvé ce dont j'ai besoin pour une application multi-pages.

Merci d'avance.

José Carrillo
la source
essayez d'utiliser le plugin requirejs.
amit_183
2
Si cela ne vous dérange pas que ReactJs soit une très grande bibliothèque JS qui devra être initialisée pour chaque page (comme vous l'avez dit, vous ne créez pas une application d'une seule page), alors je ne suis pas sûr que cela compte que vous '' ai combiné tous les composants dans un seul fichier. Il sera mis en cache sur le client. Lorsqu'une page se charge, placez- renderla dans le bon composant dans un scriptbloc.
WiredPrairie
J'ai le même problème: j'ai une application qui charge d'autres grandes bibliothèques sur différentes pages, et je préfère simplement charger react + une bibliothèque en fonction des besoins du visiteur, plutôt que quatre grandes bibliothèques au cas où.
AJFarkas

Réponses:

83

Actuellement, je fais quelque chose de similaire.

L'application n'est pas une application React complète, j'utilise React pour des éléments dynamiques, comme CommentBox, qui est autark. Et peut être inclus à tout moment avec des paramètres spéciaux.

Cependant, toutes mes sous-applications sont chargées et incluses dans un seul fichier all.js, de sorte qu'elles peuvent être mises en cache par le navigateur sur les pages.

Quand j'ai besoin d'inclure une application dans les modèles SSR, je dois juste inclure un DIV avec la classe "__react-root" et un ID spécial, (le nom de l'application React à rendre)

La logique est vraiment simple:

import CommentBox from './apps/CommentBox';
import OtherApp from './apps/OtherApp';

const APPS = {
  CommentBox,
  OtherApp
};

function renderAppInElement(el) {
  var App = APPS[el.id];
  if (!App) return;

  // get props from elements data attribute, like the post_id
  const props = Object.assign({}, el.dataset);

  ReactDOM.render(<App {...props} />, el);
}

document
  .querySelectorAll('.__react-root')
  .forEach(renderAppInElement)

<div>Some Article</div>
<div id="CommentBox" data-post_id="10" class="__react-root"></div>

<script src="/all.js"></script>

Éditer

Étant donné que Webpack prend parfaitement en charge le fractionnement de code et le LazyLoading, j'ai pensé qu'il était logique d'inclure un exemple dans lequel vous n'avez pas besoin de charger toutes vos applications dans un seul paquet, mais de les diviser et de les charger à la demande.

import React from 'react';
import ReactDOM from 'react-dom';

const apps = {
  'One': () => import('./One'),
  'Two': () => import('./Two'),
}

const renderAppInElement = (el) => {
  if (apps[el.id])  {
    apps[el.id]().then((App) => {
      ReactDOM.render(<App {...el.dataset} />, el);
    });
  }
}
webdeb
la source
1
Cela a l'air génial, est-ce que quelqu'un a exécuté cela lors de l'utilisation de npm create-react-app? Je ne pense pas que cela fonctionnerait pour moi ... Je l'ai lancé avec l'application 1 tout à l'heure et je peux voir comment cela pourrait fonctionner, mais ma version ne créera pas une version de production qui fonctionne correctement.
Sprose le
@Sprose devrait également fonctionner avec create-react-app, est-ce que je manque quelque chose? Peut-être que vous pouvez partager un petit exemple sur github là où ce n'est pas le cas, je ne vois pas pourquoi cela ne devrait pas
webdeb
1
@Sprose cherche la réponse tardive, mais je pense que vous essayez quelque chose de complètement différent, ce que cette approche tente de résoudre. IMO create react appvous propose des outils simples à créer bundle.js. Donc, le but de ma réponse est d'utiliser la même chose bundle.jset de l'utiliser sur plusieurs sites SSR, et de charger de nombreuses applications React différentes sur une seule page. Désolé, si ma réponse ne correspond pas à vos besoins, n'hésitez pas à créer un nouveau message et à décrire ce que vous essayez de faire, j'essaierais de vous aider.
webdeb
1
@SorcererApprentice cra utilise webpack, vous devez éjecter puis vérifier
webdeb
1
@SorcererApprentice, en gros, quelqu'un a posté la configuration du pack Web sur cette page: stackoverflow.com/a/41857199/5004923
webdeb le
52

Vous pouvez fournir plusieurs points d'entrée pour l'application dans le fichier webpack.config.js:

var config = {
  entry: {
    home: path.resolve(__dirname, './src/main'),
    page1: path.resolve(__dirname, './src/page1'),
    page2: path.resolve(__dirname, './src/page2'),
    vendors: ['react']
  },
 output: {
    path: path.join(__dirname, 'js'),
    filename: '[name].bundle.js',
    chunkFilename: '[id].chunk.js'
  },
}

alors vous pouvez avoir dans votre dossier src trois fichiers html différents avec leurs fichiers js respectifs (exemple pour page1):

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Page 1</title>
</head>
<body>
  <div id="app"></div>
  <script src="./vendors.js"></script>
  <script src="./page1.bundle.js"></script>
</body>
</html>

Fichier JavaScript:

import React from 'react'
import ReactDom from 'react-dom'
import App from './components/App'
import ComponentA from './components/ReactComponentA'
ReactDom.render(<div>
                  <App title='page1' />
                  <ReactComponentA/>
                 </div>, document.getElementById('app'))

Différents composants React peuvent ensuite être chargés pour chaque page.

Cocomico
la source
1
J'ai lu ce qui suit dans la page Webex. webpack version < 4 it was common to add vendors as separate entrypoint to compile it as separate file (in combination with the CommonsChunkPlugin). This is discouraged in webpack 4. Instead the optimization.splitChunks option takes care of separating vendors and app modules and creating a separate file. Do not create a entry for vendors or other stuff which is not the starting point of execution.Alors, cet exemple doit-il être mis à jour pour Webpack 4+?
Ramesh
32

Je construis une application à partir de zéro et j'apprends au fur et à mesure, mais je pense que ce que vous recherchez, c'est React-Router . React-Router mappe vos composants à des URL spécifiques. Par exemple:

render((
    <Router>
        <Route path="/" component={App}>
            <Route path="api/animals" component={Animals}>
               <Route path="birds" component={Birds}/>
               <Route path="cats" component={Cats}/>
            </Route>
        </Route>
        <Route path="api/search:term" component={AnimalSearchBox}>
    </Router>
), document.body)

Dans le cas de la recherche, 'term' est accessible en tant que propriété dans AnimalSearchBox:

componentDidMount() {
    // from the path `/api/search/:term`
    const term = this.props.params.term
}

Essaye le. Ce tutoriel est celui qui m'a mis au dessus en termes de ma compréhension de ceci et d'autres sujets connexes.


La réponse originale suit:

J'ai trouvé mon chemin ici à la recherche de la même réponse. Voyez si cet article vous inspire. Si votre application ressemble à la mienne, elle aura des zones qui changent très peu et ne varient que dans le corps principal. Vous pouvez créer un widget dont la responsabilité est de restituer un widget différent en fonction de l'état de l'application. En utilisant une architecture de flux, vous pouvez envoyer une action de navigation qui change l'état sur lequel votre widget de corps bascule, mettant à jour efficacement le corps de la page uniquement.

C'est l'approche que j'essaye maintenant.

Scott
la source
8
Que faire si le chemin de l'URL est créé par mon code backend (nodejs dans ce cas)? Est-ce que le Routertravail de la même façon qu'il le fait dans une seule application page?
Jose Carrillo
1
@Scott Que faire si je ne veux pas exposer les fonctionnalités de la page d'administration qui ont des widgets spéciaux? En utilisant react, il est possible de recréer l'apparence et la convivialité sans authentification réelle.
Kairat Kempirbaev
11

Utilisez-vous un CMS? Ils ont tendance à aimer changer les URL qui pourraient casser votre application.

Une autre façon consiste à utiliser quelque chose comme React Habitat .

Avec lui, vous pouvez enregistrer des composants et ils sont automatiquement exposés au dom.

Exemple

Enregistrer le (s) composant (s):

container.register('AnimalBox', AnimalBox);
container.register('AnimalSearchBox', AnimalSearchBox);

Ensuite, ils sont disponibles dans votre dom comme ceci:

<div data-component="AnimalBox"></div>

<div data-component="AnimalSearchBox"></div>

Ce qui précède sera automatiquement remplacé par vos composants de réaction.

Vous pouvez ensuite transmettre automatiquement des propriétés (ou accessoires) à vos composants:

<div data-component="AnimalBox" data-prop-size="small"></div>

Cela exposera sizecomme accessoire à votre composant. Il existe des options supplémentaires pour passer d'autres types tels que json, array, ints, floats, etc.

jennas
la source
2

Je sais que cela fait un moment que cette question a été posée, mais j'espère que cela aide quelqu'un.

Comme @Cocomico l'a mentionné, vous pouvez fournir plusieurs points d'entrée pour l'application dans le fichier webpack.config.js. Si vous recherchez une configuration Webpack simple (basée sur l'idée de plusieurs points d'entrée) qui vous permet d'ajouter des composants React à des pages statiques, vous pouvez envisager d'utiliser ceci: https://github.com/przemek-nowicki/multi-page -app-avec-réagir

Przemek Nowicki
la source
-1

Je vous suggère de jeter un œil à InertiaJS: https://inertiajs.com/

Avec Inertia, vous créez des applications comme vous l'avez toujours fait avec votre framework Web de choix côté serveur. Vous utilisez les fonctionnalités existantes de votre infrastructure pour le routage, les contrôleurs, le middleware, l'authentification, l'autorisation, la récupération de données, etc.

La seule chose qui est différente est votre couche de vue. Au lieu d'utiliser le rendu côté serveur (par exemple, des modèles Blade ou ERB), les vues sont des composants de page JavaScript. Cela vous permet de créer l'ensemble de votre front-end en utilisant React, Vue ou Svelte.

Agu Dondo
la source