Wie verwende ich React, um eine mehrseitige App zu erstellen?
Lesezeit: 14 Minuten
José Carrillo
Ich baue eine App mit NodeJS und möchte React für einige der interaktiven Komponenten in der gesamten Anwendung verwenden. Ich möchte es nicht zu einer Single-Page-App machen.
Wie kann ich meine React-Komponenten in einer mehrseitigen App aufteilen oder bündeln?
Derzeit befinden sich alle meine Komponenten in einer Datei, auch wenn ich sie in einigen Abschnitten der App möglicherweise nie lade.
Bisher versuche ich, bedingte Anweisungen zu verwenden, um Komponenten zu rendern, indem ich nach der ID des Containers suche, in dem React rendern wird. Ich bin mir nicht 100% sicher, was die Best Practices mit React sind. Es sieht ungefähr so aus.
Ich lese immer noch die Dokumentation und habe noch nicht gefunden, was ich für eine mehrseitige App benötige.
versuchen Sie, das requirejs-Plugin zu verwenden.
– amit_183
11. August 2015 um 8:23 Uhr
Wenn es Ihnen nichts ausmacht, dass ReactJs eine sehr große JS-Bibliothek ist, die für jede Seite initialisiert werden muss (wie Sie gesagt haben, dass Sie keine Single-Page-App erstellen), bin ich mir nicht sicher, ob es wichtig ist, dass Sie ‘ Ich habe alle Komponenten in einer Datei zusammengefasst. Es wird auf dem Client zwischengespeichert. Wenn eine Seite geladen wird, haben Sie sie render die richtige Komponente in a script Block.
– WiredPrairie
12. August 2015 um 10:32 Uhr
Ich habe das gleiche Problem: Ich habe eine App, die andere große Bibliotheken auf verschiedenen Seiten lädt, und ich würde lieber nur reagieren + eine Bibliothek je nach den Bedürfnissen des Besuchers laden, anstatt vier große Bibliotheken nur für den Fall.
– AJFarkas
2. Mai 2016 um 16:38 Uhr
webdeb
Aktuell mache ich etwas ähnliches.
Die Anwendung ist keine vollständige React-App, ich verwende React für dynamische Dinge, wie z. B. CommentBox, die autark ist. Und kann an jeder Stelle mit speziellen Parametern eingebunden werden.
Alle meine untergeordneten Apps werden jedoch geladen und in einer einzigen Datei enthalten all.jssodass es vom Browser seitenübergreifend zwischengespeichert werden kann.
Wenn ich eine App in die SSR-Vorlagen einfügen muss, muss ich nur ein DIV mit der Klasse „__react-root“ und einer speziellen ID (dem Namen der zu rendernden React-App) einfügen.
Die Logik ist ganz einfach:
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)
Da das Webpack Code-Splitting & LazyLoading perfekt unterstützt, hielt ich es für sinnvoll, ein Beispiel beizufügen, bei dem Sie nicht alle Ihre Apps in einem Bundle laden müssen, sondern sie aufteilen und bei Bedarf laden.
Sieht toll aus, hat das jemand bei der Verwendung von npm create-react-app zum Laufen gebracht? Ich glaube nicht, dass das für mich funktionieren würde … Ich habe es gerade mit der 1-App ausgeführt und kann sehen, wie es funktionieren könnte, aber mein Build erstellt keinen Produktions-Build, der ordnungsgemäß funktioniert.
– Sprössling
22. Februar 2017 um 15:30 Uhr
@Sprose sollte auch mit create-react-app funktionieren, fehlt mir etwas? Vielleicht können Sie ein kleines Beispiel auf Github teilen, wo dies nicht der Fall ist. Ich sehe keinen Grund, warum dies nicht der Fall sein sollte
– webdeb
23. Februar 2017 um 2:00 Uhr
@Sprose sry für die späte Antwort, aber ich denke, du versuchst etwas ganz anderes, was dieser Ansatz zu lösen versucht. Meiner Meinung nach create react app bietet Ihnen einige einfach zu erstellende Werkzeuge bundle.js. Der Zweck meiner Antwort besteht also darin, dasselbe zu verwenden bundle.js und verwenden Sie es auf mehreren SSR-Sites und laden Sie viele verschiedene React-Anwendungen auf einer einzigen Seite. Entschuldigung, wenn meine Antwort nicht Ihren Bedürfnissen entspricht, können Sie gerne einen neuen Beitrag erstellen und beschreiben, was Sie zu tun versuchen. Ich würde versuchen, Ihnen zu helfen.
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'))
Für jede einzelne Seite können dann unterschiedliche React-Komponenten geladen werden.
Ich habe auf der Webex-Seite über Folgendes gelesen. 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. Sollte dieses Beispiel also für Webpack 4+ aktualisiert werden?
– Ramesh
3. Oktober 2018 um 9:01 Uhr
Scott
Ich baue eine Anwendung von Grund auf und lerne dabei, aber ich denke, wonach Sie suchen React-Router. React-Router ordnet Ihre Komponenten bestimmten URLs zu. Zum Beispiel:
Im Suchfall ist „Begriff“ als Eigenschaft in der AnimalSearchBox zugänglich:
componentDidMount() {
// from the path `/api/search/:term`
const term = this.props.params.term
}
Versuch es. Diese Tutorial ist dasjenige, das mich in Bezug auf mein Verständnis dieses und anderer verwandter Themen überragend gemacht hat.
Ursprüngliche Antwort folgt:
Ich habe den Weg hierher gefunden, auf der Suche nach der gleichen Antwort. Sehen ob Dieser Beitrag inspiriert dich. Wenn Ihre Anwendung meiner ähnlich ist, wird sie Bereiche haben, die sich sehr wenig ändern und nur im Hauptteil variieren. Sie könnten ein Widget erstellen, dessen Aufgabe es ist, je nach Status der Anwendung ein anderes Widget zu rendern. Mit einer Flux-Architektur könnten Sie eine Navigationsaktion auslösen, die den Zustand ändert, in den Ihr Body-Widget wechselt, und effektiv nur den Body der Seite aktualisieren.
Das ist der Ansatz, den ich jetzt versuche.
Was ist, wenn der URL-Pfad von meinem Backend-Code (in diesem Fall nodejs) erstellt wird? Werden die Router genauso funktionieren wie in einer Single-Page-App?
– José Carrillo
22. November 2015 um 20:22 Uhr
@Scott Was ist, wenn ich keine Admin-Seiten-Funktionalität mit speziellen Widgets verfügbar machen möchte? Mit React ist es möglich, Look and Feel ohne echte Authentifizierung nachzubilden.
– Kairat Kempirbaev
16. November 2017 um 16:57 Uhr
Jenna
Verwenden Sie ein CMS? Sie neigen dazu, sich ändernde URLs zu mögen, die Ihre Anwendung beschädigen könnten.
Dies wird aussetzen size als Stütze für Ihre Komponente. Es gibt zusätzliche Optionen zum Übergeben anderer Typen wie JSON, Arrays, Ints, Floats usw.
Ich weiß, es ist eine Weile her, seit diese Frage gestellt wurde, aber hoffentlich hilft dies jemandem.
Wie @Cocomico erwähnte, könnten Sie mehrere Einstiegspunkte für die Anwendung in der Datei webpack.config.js bereitstellen. Wenn Sie nach einem einfachen Webpack-Setup suchen (basierend auf der Idee mehrerer Einstiegspunkte), mit dem Sie React-Komponenten zu statischen Seiten hinzufügen können, sollten Sie Folgendes verwenden: https://github.com/przemek-nowicki/multi-page-app-with-react
Simon Brich
Ich lasse diese alte Frage wieder aufleben, da ich in der gleichen Situation war, ohne eine Antwort zu finden, die meine Bedürfnisse befriedigen könnte. Basierend auf der Antwort von @webdeb habe ich also ein Mini-Framework geschrieben, das CRA (ohne Auswurf) verwendet, um beliebig viele Komponenten in jede HTML-Seite einzufügen und gleichzeitig alle Vorteile von CRA zu erhalten.
TL;DR
Sie können mein öffentliches Repo überprüfen hier die alle benötigten Dateien und einen Link zu einer enthält Mittlerer Artikel wo ich all diese Dinge gründlich erkläre.
Die allgemeine Idee
Der Trick besteht darin, CRA wie gewohnt zu installieren und die index.js Datei wie folgt:
import React, { Suspense } from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import reportWebVitals from './reportWebVitals';
//list here all the components that could be inserted in a web page
const apps = {
'App': React.lazy(() => import('./App')),
'TestComponent1': React.lazy(() => import('./TestComponent1')),
'TestComponent2': React.lazy(() => import('./TestComponent2')),
}
//event manager to communicate between the components
const bridgeEvent = new EventTarget();
//common fallback for all the components
function Fallback() {
return <div>Loading...</div>;
}
const renderAppInElement = (el) => {
if(apps[el.dataset.reactComponent] && !el.dataset.rendered){
//get the component's name stored in the data-react-component attribute
const App = apps[el.dataset.reactComponent];
//render the component, inject all the HTML attributes and the Event bridge
ReactDOM.render(
<Suspense fallback={<Fallback />}>
<App {...el.dataset} bridgeEvent={bridgeEvent}/>
</Suspense>
, el);
el.dataset.rendered = true;
}
else if(el.dataset.rendered){
console.log('el', el, 'is already rendered')
}
}
//ONLY FOR THE DEV PHASE
const rootEl = document.getElementById('root');
//generate components without attributes
if(process.env.REACT_APP_RENDER_CMP){
const components = process.env.REACT_APP_RENDER_CMP.split(',');
components.forEach(item => {
const componentEl = document.createElement('div');
componentEl.setAttribute("data-react-component", item);
componentEl.className = "__react-cmp";
rootEl.append(componentEl);
});
}
//generate components with attributes
if(process.env.REACT_APP_RENDER_CMP_WITH_ATTRS){
let componentsWithAttrs;
try{
componentsWithAttrs = JSON.parse(process.env.REACT_APP_RENDER_CMP_WITH_ATTRS);
}
catch(e){
console.log('fail to parse REACT_APP_RENDER_CMP_WITH_ATTRS', e);
}
if(componentsWithAttrs){
componentsWithAttrs.forEach(cmp => {
const componentEl = document.createElement('div');
componentEl.setAttribute("data-react-component", cmp.class);
componentEl.className = "__react-cmp";
Object.keys(cmp.data).forEach(attrKey => {
componentEl.setAttribute(attrKey, cmp.data[attrKey]);
});
rootEl.append(componentEl);
});
}
}
//the default name of the global object is ReactComponents, but it could be customized via the REACT_APP_NAMESPACE environment variable
const appNamespace = process.env.REACT_APP_NAMESPACE || "ReactComponents";
window[appNamespace] = {
ready: false,
parseComponents(container){
//parse the container or the whole document and inject all the components in the containers that have a "__react-cmp" class
(container || document)
.querySelectorAll('.__react-cmp')
.forEach(renderAppInElement);
}
}
window[appNamespace].parseComponents();
window[appNamespace].ready = true;
//if dynamic parsing must be done via the window.ReactComponents.parseComponents() method
//check the availability of window.ReactComponents object via window.ReactComponents.ready property
//or define a window.ReactComponentsAsyncInit() method to be notified of the availability
if(typeof window[`${appNamespace}AsyncInit`] === 'function'){
window[`${appNamespace}AsyncInit`]();
}
// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
reportWebVitals();
Dann können Sie hinzufügen REACT_APP_RENDER_CMP und/oder REACT_APP_RENDER_CMP_WITH_ATTRS Umgebungsvariablen, um Ihre Komponenten zu testen, während Sie den Entwicklungsserver des CRA verwenden. Dein .env.development.local Datei könnte so aussehen:
#this will render the TestComponent1 and TestComponent2 without any attributes
REACT_APP_RENDER_CMP="TestComponent1,TestComponent2"
#this will render TestComponent1 with the data-test-attribute attribute set to "test attribute value"
REACT_APP_RENDER_CMP_WITH_ATTRS="[{"class":"TestComponent1","data":{"data-test-attribute":"test attribute value"}}]"
Nachdem Sie Ihre Dateien erstellt haben, sollten Sie Ihre haben index.html Datei mit allen .js und .css Dateien, die Sie in jede Seite Ihrer mehrseitigen App einfügen müssen, die Ihre React-Komponenten laden soll. Vergessen Sie nicht hinzuzufügen INLINE_RUNTIME_CHUNK=false in deinem .env Datei, um Inline-Javascript zu vermeiden!
Fügen Sie dann die Container der Komponenten in den HTML-Seiten dort hinzu, wo sie angezeigt werden sollen. Zum Beispiel:
Die parseComponents() in den CRAs deklariert index.js Datei ausgeführt werden sollte, schnappen Sie sich Ihre div mit dem .__react-cmp Klasse, und verwenden Sie sie dann als Container für Ihre TestComponent1 Reaktionskomponente.
Im gewidmet Repo und Artikel Ich erkläre, wie Sie Ihren Build-Pfad mit den CRAs ändern können BUILD_PATH Umgebungsvariable (damit Sie Ihre erstellten Dateien auf Ihrem Server oder in einem CDN hosten können) und ich stelle einen Loader bereit, der die erstellten Dateien analysiert index.html Datei und fügen Sie alle erforderlichen dynamisch ein .js und .css Dateien in Ihrer Seite (Sie müssen also nur den Loader einbinden, anstatt alle Dateien). So sieht der Loader aus, vorausgesetzt, sein Dateiname lautet cmp-loader.js und neben Ihrem Build gehostet index.html Datei:
(async () => {
const head = document.getElementsByTagName('head')[0];
const scriptSrcRegexp = new RegExp('<script.*?src="(.*?)"', 'gmi');
//get the exact script's src as defined in the src attribute
const scriptSrc = scriptSrcRegexp.exec(document.currentScript.outerHTML);
//all the resources should be relative to the path of this script
const resourcesPath = (scriptSrc && scriptSrc.length > 1) ? scriptSrc[1].replace('cmp-loader.js', '') : '';
//get the index content
const indexHTML = await (await fetch(resourcesPath+'index.html', {cache:'reload'})).text();
//assume that all the .js and .css files to load are in the "static" folder
const reactCSSRegexp = new RegExp(`<link href="${resourcesPath}static\/css\/(.*?)\.css" rel="stylesheet">`, 'gm');
const reactJSRegexp = new RegExp(`<script (.*?) src="${resourcesPath}static\/js\/(.*?)\.js"><\/script>`, 'gm');
//grab all the css tags
const ReactCSS = [].concat(indexHTML.match(reactCSSRegexp)).join('');
//grab all the js tags
const ReactJS = [].concat(indexHTML.match(reactJSRegexp)).join('');
//parse and execute the scripts
const scriptsDoc = new DOMParser().parseFromString(ReactJS, 'text/html');
Array.from(scriptsDoc.getElementsByTagName('script')).forEach(item => {
const script = document.createElement('script');
[...item.attributes].forEach(attr => {
script.setAttribute(attr.name, attr.value)
})
head.appendChild(script);
});
//inject the CSS
head.insertAdjacentHTML('beforeend', ReactCSS);
})().catch(e => {
console.log('fail to load react-cmp', e)
});
Mit Inertia erstellen Sie Apps genau so, wie Sie es immer mit Ihrem serverseitigen Web-Framework Ihrer Wahl getan haben. Sie verwenden die vorhandene Funktionalität Ihres Frameworks für Routing, Controller, Middleware, Authentifizierung, Autorisierung, Datenabruf und mehr.
Das einzige, was anders ist, ist Ihre Ansichtsebene. Anstatt serverseitiges Rendering (z. B. Blade- oder ERB-Vorlagen) zu verwenden, sind die Ansichten JavaScript-Seitenkomponenten. Auf diese Weise können Sie Ihr gesamtes Frontend mit React, Vue oder Svelte erstellen.
14390500cookie-checkWie verwende ich React, um eine mehrseitige App zu erstellen?yes
versuchen Sie, das requirejs-Plugin zu verwenden.
– amit_183
11. August 2015 um 8:23 Uhr
Wenn es Ihnen nichts ausmacht, dass ReactJs eine sehr große JS-Bibliothek ist, die für jede Seite initialisiert werden muss (wie Sie gesagt haben, dass Sie keine Single-Page-App erstellen), bin ich mir nicht sicher, ob es wichtig ist, dass Sie ‘ Ich habe alle Komponenten in einer Datei zusammengefasst. Es wird auf dem Client zwischengespeichert. Wenn eine Seite geladen wird, haben Sie sie
render
die richtige Komponente in ascript
Block.– WiredPrairie
12. August 2015 um 10:32 Uhr
Ich habe das gleiche Problem: Ich habe eine App, die andere große Bibliotheken auf verschiedenen Seiten lädt, und ich würde lieber nur reagieren + eine Bibliothek je nach den Bedürfnissen des Besuchers laden, anstatt vier große Bibliotheken nur für den Fall.
– AJFarkas
2. Mai 2016 um 16:38 Uhr