React.js – Server-Side Rendering A bis Z 2019

React.js – Server-Side Rendering A bis Z 2019


Mit etwas Kreativität eignet sich React für eine Vielzahl von Anwendungsmöglichkeiten. Doch dem Standard Package sind gewisse Grenzen gesetzt. Spätestens beim Thema SEO und HTTP Status Codes stoßen wir schnell an unsere Grenzen. Mit Server-Side Rendering können wir einige Klippen umschiffen. Dieser Guide soll euch eine Vorstellung geben wie ihr euer eigenes Boilerplate für React mit Server Side Rendering erstellt, ordentlich konfiguriert und möglichst komfortabel an eurer App arbeiten könnt, fast wie mit dem Standard CRA Package.

Das Problem – Client Side Rendering

React wird wie die meisten Javascript Frameworks Clientseitig gerendert. Eure Nutzer bekommen beim Aufruf eurer Website also erstmal eure komplette „Web-app“, aber nur eine sperrliche Index.html. Sobald alles geladen wurde übernimmt React und füllt eure Seite, aber eben Clientseitig. Das bedeutet zum einen, dass der Server nicht mehr gefragt wird, ob eine Seite existiert oder weiterleitet etc. Zum anderen sind Suchmaschinen meiner Erfahrung nach noch nicht darauf ausgelegt euren Javascript Code auszuführen. Suchmaschinen werden also immer nur im ersten Durchlauf eure leere Index.html lesen und keinen Informationsgehalt daraus ziehen. Das hat weiter zur Folge, dass die Suchmaschine den Inhalt verschiedener Unterseiten nicht unterscheiden kann und euch im Worst Case für doppelten Content abstraft. Das mag sich in Zukunft ändern, aber so lange Suchmaschinen euren Javascript Code nicht interpretieren bleibt euch keine andere Wahl als auf statische Seiten zu setzen oder serverseitig zu rendern.

Die Lösung

Wir werden einen Server aufsetzen, der die URL interpretiert, den passenden Inhalt auf dem Server rendert und das vollständige Ergebnis an den User oder die Suchmaschine übergibt. Für den User macht es im weiteren Verlauf keinen Unterschied. Sobald die Seite geladen wurde wird wie gewohnt React übernehmen und flüssig und schnell den Content bereitstellen. Suchmaschinen sind für die Bereitstellung des Inhalts aber äußerst dankbar. Denn auch sie bekommen den gerenderten Inhalt zu sehen. Des Weiteren habt ihr so die Möglichkeit HTTP Statuscodes zu setzen, z.B. für Seiten die nicht verfügbar sind einen 404 zu versenden. Darüber hinaus könnt ihr euch für jede Seite um SEO Inhalte bemühen wie Metatags und strukturierte Daten.

1. Projekt Initialisieren

Leg einen neuen Ordner für dein Projekt an, öffne ihn in einem Codeeditor deiner Wahl (ich empfehle Visual Studio Code) und initiiere dein Projekt

Dieser Vorgang erstellt euer package.json. Diese sieht ähnlich aus wie folgende:

2. Server aufsetzen

In eurem Projektordner erstellt ihr einen Ordner /src und darin einen Ordner /server. Darin erstellt ihr eine Datei server.js.
Die Ordnerstruktur sieht nun so aus:

Für den Anfang installieren wir express.js als Server.

öffnet nun server.js und setzt den Server auf:

Wenn wir später Webpack mit Babel installieren können wir die Syntax auf die gewohnte ES6 Syntax abändern. Für den Moment sollte es aber genügen. Wir können unseren Server starten und testen.
Startet den Server mit

Öffnet einen Browser und ruft die Seite localhost:3000 auf. Dort wird die Meldung Cannot GET / erscheinen. Daran arbeiten wir als nächstes.

3. Serverseitig rendern

Wir bleiben in server.js und fügen nach dem PORT und vor app.listen... folgende Zeilen ein:

Achtet auf die bei der html Konstanten.

app.get(...) fängt alle URL Abfragen ab und verarbeitet diese. Mit res.send() senden wir die Antwort vom Server. In diesem Fall senden wir den Inhalt der Variable html. Diese wird unsere index.html ersetzen, die wir aus dem Standard CRA Package kennen.

Ein neuer Test mach deutlich was wir gemacht haben. Startet den Server erneut wie oben beschrieben. Geht auf die Seite und siehe da…Hello Server sollte euch nun begrüßen.

4. React einbinden

Jetzt wird es spannend. Wir legen einen neuen Ordner /client im src-Ordner an. Darin erstellen wir die folgenden 3 Dateien: index.js, App.js und index.css. Eure Ordnerstruktur sieht nun so aus:

Nun installieren wir die notwendigen Pakete für React und React-Router:

  • react
  • react-router
  • react-router-dom
  • react-dom

Die App.js könnt ihr fast identisch zum CRA – Standard anlegen. Wir wollen aber gleich den <Switch> und die erste <Route /> anlegen, damit wir gleich den Router integrieren:

Die index.js sieht ebenfalls fast wie das CRA Original aus. Wir verwenden allerdings hydrate statt render, damit React nur den Teil des DOMs updatet, der sich tatsächlich vom Original unterscheidet. An dieser Stelle wickeln wir die <App /> auch gleich in den <BrowserRouter> ein:

In der index.css könnt ihr euch austoben wie ihr wollt. Wir kommen später zu einigen Besonderheiten.

Nun sind ein paar Anpassungen an server.js notwendig, damit der Server die React App rendert und das Ergebnis an den Client sendet:

Gehen wir die Änderungen durch:

Wir ändern schon mal die Syntax zu ES6. So wird aus

Dazu kommen die Importe für unsere React App.
In der app.get(...) Methode ergänzen wir die Konstanten context, die für den HTTP Status Code relevant wird und erzeugen in content mit der renderToString() Methode, dem <StaticRouter>und der location unseren eigentlichen Inhalt für die Seite.
Im html Boilerplate richten wir das bekannte <div id='root'> ein, welches Clientseitig verwendet wird um den Inhalt clientseitig zu rendern.
Innerhalb des Tags setzen wir den ${content} als variable. Dieser wird den angefragten Inhalt, der serverseitig gerendert wurde initial an den Client übergeben. So kann auch eine Suchmaschine euren Inhalt lesen, denn sie bekommt den kompletten angefragten Content direkt vom Server. Interagiert euer User nun weiter mit der Seite, wird React mit dem <div id='root'> Container wie gewohnt arbeiten können.

Noch können wir es nicht testen, denn euer Server kann die ES6 Syntax nicht interpretieren. Darum kümmern wir uns im nächsten Schritt.

5. Webpack und Babel installieren und einrichten

Erst mal brauchen wir eine ganze reihe von DevDependencies:

  • @babel/core
  • @babel/preset-env
  • @babel/preset-react
  • babel-loader
  • webpack
  • webpack-cli
  • mini-css-extract-plugin
  • css-loader
  • clean-webpack-plugin

Außerdem verwenden wir nodemon um den Server automatisch zu starten:

Das ist eine Menge, achtet bei der Installation auf --save-dev und auf die richtige Schreibweise. Dieser Guide bezieht sich auf die aktuellen Versionen von 2019. Es hat mich bei der Suche nach einem Tutorial wahnsinnig gemacht, dass größtenteils veraltete Versionen und damit auch veraltete Herangehensweisen verwendet wurden. Achtet also bitte auf die Versionen. Hier ist meine package.json damit ihr euch orientieren könnt. Weichen eure Versionen zu sehr ab, könnte es sein, dass es so nicht mehr funktioniert. Ich werde mich bemühen diesen Beitrag zu updaten, wenn sich grundlegend etwas ändert:

Webpack benutzen wir zum einen um den babel-loader einzubinden, der die ES6 Syntax für den Browser interpretierbar macht. Außerdem nutzen wir Webpack um ein fertiges Build erstellen zu können, welches ihr dann auf euren Server schieben könnt, assets wie css und später auch Bilder oder Schriften sauber zu organisieren und einzubinden.

In eurem Stammverzeichnis benötigen wir jetzt zwei neue Dateien:

  • .babelrc
  • webpack.config.js

Euer Projektordner sieht nun also so aus:

In eure .babelrc schreibt ihr folgende Zeilen:

Eure webpack.config.js kann folgendermaßen aussehen:

Zu Beginn werden die Plugins geladen. Die path Variable brauchen wir für den aktuellen Verzeichnis Pfad. __dirname setzen wir auf false, weil ihr sonst Probleme mit der richtigen Verzeichnisangabe bekommt. Entry ist einmal euer Server mit server.js und euer Client mit index.js.

In den Modulen übersetzen wir alle .js oder .jsx Dateien mit dem babel-loader. .css Dateien werden mit dem MiniCssExtractPlugin.loader verarbeitet. In den Plugin Einstellungen verwenden wir CleanWebpackPlugin, damit sich euer Build ordner immer säubert, bevor ihr ein neues Build erstellt, damit keine alten Dateien da drin rum liegen. Wir setzen ignoreOrder auf false, damit Klassennamen in der CSS Datei nicht umbenannt werden, was sonst die normale Folge wäre.

Ihr könnt bei der Vergabe der Output Namen frei entscheiden wie die Dateien heißen sollen und wo sie liegen. Wenn ihr einfach diesem Guide weiter folgen wollt, empfehle ich diese Angaben nicht zu ändern.

Nun richten wir uns noch in der package.json zwei Skripte ein, die uns die Arbeit etwas leichter machen:

Vielleicht habt ihr sie oben schon bemerkt. Das webpack:build erzeugt euch ein Build, welches auf einen echten Server geladen werden kann. Mit server wird diese Version dann gestartet.

Ereugt nun euer erstes Build und startet dann euren Server:

Wenn alles gut gegangen ist, könnt ihr nun euer Ergebnis im Browser bewundern. Vielleicht sieht es noch nicht anders aus als vorher, aber es ist ein gewaltiger unterschied. Schaut euch den Source-Code an. Dort wird der Inhalt aus eurer App.js Datei angezeigt. Dieser wurde erst gerendert und dann an den Browser übergeben. Mit der Standard CRA, würdet ihr im Source-Code nur den leeren <div id='root'> Container sehen.

6. React Funktionalität übertragen

Ein wichtiger Schritt in der server.js steht noch aus, damit React wie gewohnt in eurer App funktioniert:

Vor app.get(...) ergänzt ihr app.use(...) und legt damit den öffentlichen Pfad eurer App fest, damit niemand Zugriff auf die server.js eures Buildordners bekommt, denn dort können sensible Daten abgelegt sein.
Im html Boilerplate ergänzt ihr noch das Script zu eurer client.js um die React Funktionalität zu ergänzen.

Das wars schon

Ihr habt eine lauffähige, serverseitig gerenderte React App. In den nächsten Folgen bauen wir auf dieser Struktur auf und bauen unsere App weiter auf. Wir behandeln HTTP Status Codes, dynamische Metatags, Strukturierte Daten und binden weitere Assets wie Schriften und Bilder ein. Wir kümmern uns um Redux und Material-UI und ich zeige euch so, wie ihr eigentlich schon ab diesem Zeitpunkt wie gewohnt weiter entwickeln könnt und am wie man einen express Server selber hosten kann, ohne Heroku oder andere Dienstleister.

Leave a comment



dreizack-logo-2

Ihr Netzwerkpartner für digitale Medien und eCommerce aus Halle (Saale).

Nachricht an uns

Datenschutz

Kontakt