React ist am besten als clientseitiges JavaScript-Framework bekannt, aber wussten Sie, dass Sie React serverseitig rendern können (und vielleicht sogar sollten!)?
Stellen Sie sich vor, Sie haben für einen Kunden eine blitzschnelle neue React-App zur Veranstaltungsliste entwickelt. Die App ist mit einem von Ihnen bevorzugten serverseitigen Tool an eine API angebunden. Ein paar Wochen später teilt Ihnen der Kunde mit, dass seine Seiten nicht bei Google angezeigt werden und beim Posten auf Facebook nicht gut aussehen. Scheint lösbar, oder?
Sie stellen fest, dass Sie, um dies zu lösen, Ihre React-Seiten beim ersten Laden vom Server rendern müssen, damit Crawler von Suchmaschinen und Social-Media-Seiten Ihr Markup lesen können. Es gibt Hinweise darauf, dass Google *manchmal* JavaScript ausführt und den generierten Inhalt indizieren kann, aber nicht immer. Daher wird serverseitiges Rendering immer empfohlen, wenn Sie eine gute SEO und Kompatibilität mit anderen Diensten wie Facebook und Twitter gewährleisten möchten.
In diesem Tutorial führen wir Sie Schritt für Schritt durch ein serverseitiges Rendering-Beispiel, einschließlich der Überwindung eines häufigen Hindernisses für React-Apps, die mit APIs kommunizieren.
Die Vorteile von Server-Side Rendering
SEO mag der Grund sein, der Ihr Team dazu bringt, über serverseitiges Rendering zu sprechen, aber es ist nicht der einzige potenzielle Vorteil.
Hier ist der wichtigste: **Serverseitiges Rendering zeigt Seiten schneller an**. Beim serverseitigen Rendering ist die Antwort Ihres Servers an den Browser das HTML Ihrer Seite, das zum Rendern bereit ist, sodass der Browser mit dem Rendern beginnen kann, ohne auf den Download und die Ausführung des gesamten JavaScripts warten zu müssen. Es gibt keine "weiße Seite", während der Browser das JavaScript und andere für das Rendern der Seite benötigte Assets herunterlädt und ausführt, was bei einer rein clientseitig gerenderten React-Site passieren könnte.
Erste Schritte
Gehen wir durch, wie Sie serverseitiges Rendering zu einer einfachen clientseitig gerenderten React-App mit Babel und webpack hinzufügen. Unsere App wird die zusätzliche Komplexität des Abrufens von Daten von einer Drittanbieter-API mit sich bringen.
Hinweis des Herausgebers: Dieser Beitrag stammt von einem CMS-Unternehmen, und ich habe ziemlich spammy E-Mails von ihnen erhalten, die ich als sehr uncool empfinde. Daher entferne ich alle Verweise auf sie in diesem Artikel und ersetze sie durch generische "CMS"-Terminologie.
import React from 'react';
import cms from 'cms';
const content = cms('b60a008584313ed21803780bc9208557b3b49fbb');
var Hello = React.createClass({
getInitialState: function() {
return {loaded: false};
},
componentWillMount: function() {
content.post.list().then((resp) => {
this.setState({
loaded: true,
resp: resp.data
})
});
},
render: function() {
if (this.state.loaded) {
return (
<div>
{this.state.resp.data.map((post) => {
return (
<div key={post.slug}>{post.title}</div>
)
})}
</div>
);
} else {
return <div>Loading...</div>;
}
}
});
export default Hello;
Hier ist, was der Starter-Code noch enthält
- `package.json` – für Abhängigkeiten
- Webpack- und Babel-Konfiguration
- `index.html` – das HTML für die App
- `index.js` – lädt React und rendert die
Hello-Komponente
Um die App zum Laufen zu bringen, klonen Sie zuerst das Repository
git clone ...
cd ..
Installieren Sie die Abhängigkeiten
npm install
Starten Sie dann den Entwicklungsserver
npm run start
Rufen Sie https://:3000 auf, um die App anzuzeigen

Wenn Sie den Quellcode der gerenderten Seite anzeigen, werden Sie feststellen, dass das an den Browser gesendete Markup nur ein Link zu einer JavaScript-Datei ist. Dies bedeutet, dass die Inhalte der Seite nicht garantiert von Suchmaschinen und Social-Media-Plattformen gecrawlt werden können

Hinzufügen von Server Side Rendering
Als Nächstes implementieren wir serverseitiges Rendering, damit vollständig generiertes HTML an den Browser gesendet wird.
Um zu beginnen, installieren wir Express, ein serverseitiges Anwendungsframework für Node.js
npm install express --save
Wir möchten einen Server erstellen, der unsere React-Komponente rendert
import express from 'express';
import fs from 'fs';
import path from 'path';
import React from 'react';
import ReactDOMServer from 'react-dom/server';
import Hello from './Hello.js';
function handleRender(req, res) {
// Renders our Hello component into an HTML string
const html = ReactDOMServer.renderToString(<Hello />);
// Load contents of index.html
fs.readFile('./index.html', 'utf8', function (err, data) {
if (err) throw err;
// Inserts the rendered React HTML into our main div
const document = data.replace(/<div id="app"><\/div>/, `<div id="app">${html}</div>`);
// Sends the response back to the client
res.send(document);
});
}
const app = express();
// Serve built files with static files middleware
app.use('/build', express.static(path.join(__dirname, 'build')));
// Serve requests with our handleRender function
app.get('*', handleRender);
// Start server
app.listen(3000);
Lassen Sie uns aufschlüsseln, was passiert...
Die Funktion handleRender verarbeitet alle Anfragen. Die Klasse ReactDOMServer, die am Anfang der Datei importiert wird, stellt die Methode renderToString() bereit, die ein React-Element in sein anfängliches HTML rendert.
ReactDOMServer.renderToString(<Hello />);
Dies gibt das HTML für die Hello-Komponente zurück, das wir in das HTML von index.html einfügen, um das vollständige HTML für die Seite auf dem Server zu generieren.
const document = data.replace(/<div id="app"><\/div>/, `<div id="app">${html}</div>`);
Um den Server zu starten, aktualisieren Sie das Startskript in `package.json` und führen Sie dann `npm run start` aus
"scripts": {
"start": "webpack && babel-node server.js"
},
Rufen Sie https://:3000 auf, um die App anzuzeigen. Voilà! Ihre Seite wird jetzt vom Server gerendert. Aber es gibt ein Problem. Wenn Sie den Quellcode der Seite im Browser anzeigen. Sie werden feststellen, dass die Blogbeiträge immer noch nicht in der Antwort enthalten sind. Was ist los? Wenn wir den Netzwerk-Tab in Chrome öffnen, sehen wir, dass die API-Anfrage auf dem Client erfolgt.

Obwohl wir die React-Komponente auf dem Server rendern, wird die API-Anfrage *asynchron* in componentWillMount gemacht und die Komponente wird gerendert, *bevor* die Anfrage abgeschlossen ist. Obwohl wir also auf dem Server rendern, tun wir dies nur teilweise. Es stellt sich heraus, dass es ein Problem im React-Repository gibt, mit über 100 Kommentaren, die das Problem und verschiedene Workarounds diskutieren.
Daten vor dem Rendering abrufen
Um dies zu beheben, müssen wir sicherstellen, dass die API-Anfrage abgeschlossen ist, bevor die Hello-Komponente gerendert wird. Das bedeutet, die API-Anfrage außerhalb des Render-Zyklus der React-Komponente zu stellen und Daten abzurufen, bevor wir die Komponente rendern.
Um den Datenabruf vor das Rendering zu verlagern, installieren wir react-transmit
npm install react-transmit --save
React Transmit bietet uns elegante Wrapper-Komponenten (oft als "Higher-Order Components" bezeichnet) zum Abrufen von Daten, die sowohl auf dem Client als auch auf dem Server funktionieren.
So sieht unsere Komponente mit implementiertem React Transmit aus
import React from 'react';
import cms from 'cms'
import Transmit from 'react-transmit';
const content = cms('b60a008584313ed21803780bc9208557b3b49fbb');
var Hello = React.createClass({
render: function() {
if (this.props.posts) {
return (
<div>
{this.props.posts.data.map((post) => {
return (
<div key={post.slug}>{post.title}</div>
)
})}
</div>
);
} else {
return <div>Loading...</div>;
}
}
});
export default Transmit.createContainer(Hello, {
// These must be set or else it would fail to render
initialVariables: {},
// Each fragment will be resolved into a prop
fragments: {
posts() {
return content.post.list().then((resp) => resp.data);
}
}
});
Wir haben unsere Komponente in eine Higher-Order-Komponente eingewickelt, die Daten mit Transmit.createContainer abruft. Wir haben die Lifecycle-Methoden aus der React-Komponente entfernt, da kein Grund besteht, Daten zweimal abzurufen. Und wir haben die render-Methode geändert, um props-Referenzen anstelle von state zu verwenden, da React Transmit die Daten als Props an die Komponente übergibt.
Um sicherzustellen, dass der Server Daten vor dem Rendering abruft, importieren wir Transmit und verwenden Transmit.renderToString anstelle der ReactDOM.renderToString-Methode.
import express from 'express';
import fs from 'fs';
import path from 'path';
import React from 'react';
import ReactDOMServer from 'react-dom/server';
import Hello from './Hello.js';
import Transmit from 'react-transmit';
function handleRender(req, res) {
Transmit.renderToString(Hello).then(({reactString, reactData}) => {
fs.readFile('./index.html', 'utf8', function (err, data) {
if (err) throw err;
const document = data.replace(/<div id="app"><\/div>/, `<div id="app">${reactString}</div>`);
const output = Transmit.injectIntoMarkup(document, reactData, ['/build/client.js']);
res.send(document);
});
});
}
const app = express();
// Serve built files with static files middleware
app.use('/build', express.static(path.join(__dirname, 'build')));
// Serve requests with our handleRender function
app.get('*', handleRender);
// Start server
app.listen(3000);
Starten Sie den Server neu, rufen Sie https://:3000 auf. Zeigen Sie den Quellcode der Seite an und Sie werden sehen, dass die Seite jetzt vollständig auf dem Server gerendert wird!

Weiter geht's
Wir haben es geschafft! Die Verwendung von React auf dem Server kann knifflig sein, insbesondere beim Abrufen von Daten aus APIs. Glücklicherweise ist die React-Community sehr aktiv und entwickelt viele hilfreiche Tools. Wenn Sie an Frameworks für die Erstellung großer React-Apps interessiert sind, die auf Client und Server gerendert werden, schauen Sie sich Electrode von Walmart Labs oder Next.js an. Oder wenn Sie React in Ruby rendern möchten, schauen Sie sich AirBnBs Hypernova an.
Ich wollte nur darauf hinweisen, dass der Benutzer technisch gesehen immer noch eine "weiße Seite" sehen wird. Wenn sie Ihre Domain aufrufen, muss der Benutzer immer noch darauf warten, dass die Anfrage an Ihren Server gestellt wird, und dann, dass Ihr Server die Anfrage an die API stellt, dann werden die Daten an den Benutzer zurückgesendet. Der Benutzer sieht während dieser Zeit nichts auf seinem Bildschirm. Wenn die Anfrage an die Drittanbieter-API langsam ist, wäre es meiner Meinung nach eine bessere Erfahrung, Ihren Anwendungscode von einem CDN zurückzugeben, damit Ihre leere Benutzeroberfläche so schnell wie möglich angezeigt werden kann, und dann können Sie dem Benutzer zumindest einen Spinner anzeigen, während Sie auf die Drittanbieter-API warten.
Ich stimme zu, übrigens, wenn die betroffene API auf demselben Server ist, kann die Anfrage sehr schnell sein, was ein guter Punkt für serverseitiges Rendering ist :)
Das Problem Ihrer Lösung ist die leere Benutzeroberfläche, die für SEO ziemlich schlecht ist.
Ich denke, bei Apps mit vielen API-Anfragen mit heterogenen Antwortzeiten müssen wir eine Mischung aus serverseitigen und clientseitigen Anfragen machen, wobei Anfragen, die für SEO und/oder die oberste Ebene der Benutzeroberfläche unerlässlich sind, beim serverseitigen Rendering Vorrang haben.
Wenn Sie sich Sorgen um den zusätzlichen Roundtrip für den Server machen, um Daten von einem Drittanbieterdienst zurückzugeben, können Sie die Benutzeroberfläche immer zuerst auf dem Client rendern, ohne die Drittanbieterkomponente (möglicherweise mit einem temporären Spinner). Schieben Sie eine Client-Anfrage für DOMContentLoaded oder das onload-Ereignis, um den Server zu bitten, die Transaktion durchzuführen. Es gibt keine Möglichkeit, Netzwerkverzögerungen zu umgehen (es sei denn, Sie verwenden das Netzwerk nicht, was meine Antwort für diesen Anwendungsfall wäre ;), aber ich sehe nicht, warum Sie sie nicht so verschieben können, dass sie zu jedem gewünschten Zeitpunkt auftreten.
Episode 15 von Front End Center (nur kostenpflichtig) enthält ein Video mit dem Titel "The Hidden Costs of Client-Side Rendering", das sich mit all dem befasst, mit einer anderen React-basierten Bibliothek für statisches HTML-Rendering. Ein faszinierendes Thema, und ich bin froh, dass die Leute es ernst nehmen und es aus verschiedenen Blickwinkeln angehen.
SEO ist immer ein Hauptgrund für serverseitiges Rendering. Wenn jedoch die Ladezeit ein Anliegen ist, wäre es dann nicht vorteilhaft, eine Form des clientseitigen Cachings für Ihre Anwendungsdaten zu verwenden? Service Worker adressieren dieses Problem zusätzlich zur Einsparung von Datenbandbreite, da nicht alle Anwendungsdaten bei jeder anfänglichen Seitenladung benötigt werden.
Auch wenn iOS bei Service Workern langsam war, würde das serverseitige Rendering immer noch einen kleinen Geschwindigkeitsschub bringen, während Android- und PC-Benutzer eine deutliche Verbesserung der Antwortzeiten sehen würden. Darüber hinaus würden Service Worker die Tür für die Offline-Nutzung einer Anwendung sowie für erweiterte native mobile Funktionalität öffnen.
Nur eine Anmerkung, aber Sie sollten keine API in componentWillMount aufrufen. Wenn Ihr Aufruf einen Fehler zurückgeben würde, hätten Sie eine Menge Spaß :)
Hallo Jungs, vergesst die ganze Arbeit und nehmt einen Dienst, der alles für euch erledigt
https://www.roast.io
Pre-render
Host
Lächeln
Ich war Teil der Beta, und ich weiß, dass der Kerl sich sehr freuen würde, wenn die Community sich das zu eigen machen und damit weitermachen würde!!
SPAs sind eine Abscheulichkeit für das Web. SSR, wenn Sie wollen, aber Sie fügen Ihrem Code nur Komplexität hinzu, wenn Sie von Anfang an mit PJAX hätten bauen sollen. Teehee.