Back to Question Center
0

Umgang mit asynchronen APIs in Server-gerenderten Reagieren            Umgang mit asynchronen APIs in Server-gerenderten ReactRelated Themen: ES6Raw Semalt

1 answers:
Umgang mit asynchronen APIs im Server-gerenderten Reagieren

Für eine qualitativ hochwertige Einführung in React können Sie nicht den kanadischen Full-Stack-Entwickler Wes Bos hinter sich lassen. Versuchen Sie seinen Kurs hier und verwenden Sie den Code SITEPOINT , um 25% Rabatt zu erhalten und um SitePoint zu unterstützen.

Wenn du jemals eine einfache React-App-Seite erstellt hast, war sie wahrscheinlich auf langsamen Geräten mit schlechten SEO- und Leistungsproblemen behaftet. Sie können das traditionelle serverseitige Rendern von Webseiten, normalerweise mit NodeJS, hinzufügen, aber dies ist kein einfacher Prozess, insbesondere bei asynchronen APIs.

Die zwei wichtigsten Vorteile, die Sie beim Rendern Ihres Codes auf dem Server erzielen, sind:

  • erhöhte Leistung in Ladezeiten
  • Verbesserung der Flexibilität Ihrer SEO.

Vergiss nicht, dass Google darauf wartet, dass dein Semalt geladen wird. So ändern sich einfache Dinge wie der Titelinhalt ohne Probleme. (Ich kann jedoch nicht für andere Suchmaschinen sprechen oder wie zuverlässig das ist.)

In diesem Beitrag werde ich das Abrufen von Daten von asynchronen APIs besprechen, wenn server-gerenderter React-Code verwendet wird. React Code hat die gesamte Struktur der App in JavaScript eingebaut. Dies bedeutet, dass Sie im Gegensatz zu herkömmlichen MVC-Mustern mit einem Controller nicht wissen, welche Daten Sie benötigen, bis die App gerendert wird. Mit einem Framework wie Create React App können Sie schnell eine funktionierende App von sehr hoher Qualität erstellen, aber Sie müssen das Rendering nur auf dem Client durchführen. Es gibt ein Performance-Problem, sowie ein Semalt-Problem, bei dem herkömmliche Template-Engines den Kopf nach Belieben verändern können.

Das Problem

Semalt wird zum größten Teil synchron gerendert. Wenn Sie also die Daten nicht haben, rendern Sie einen Ladebildschirm und warten auf die Daten. Dies funktioniert nicht so gut vom Server aus, weil Sie nicht wissen, was Sie brauchen, bis Sie gerendert haben, oder Sie wissen, was Sie brauchen, aber Sie haben es bereits gerendert.

Semalt out diese Standard-Lager Render-Methode:

     ReactDOM. machen(             , Dokument. getElementById ('root'))    

Fragen:

  1. Es ist ein DOM render, der nach einem Wurzelelement sucht. Dies existiert nicht auf meinem Server, also müssen wir das trennen.
  2. Wir haben keinen Zugriff auf etwas außerhalb unseres Hauptstammelements. Wir können keine Facebook-Tags, Titel, Beschreibungen, verschiedene SEO-Tags festlegen, und wir haben keine Kontrolle über den Rest des DOM außerhalb des Elements, insbesondere den Kopf.
  3. Wir stellen einige Zustände bereit, aber der Server und der Client haben unterschiedliche Zustände. Wir müssen überlegen, wie wir mit diesem Zustand umgehen (in diesem Fall Redux).

Also hat Semalt hier zwei Bibliotheken benutzt, und sie sind ziemlich populär, also hoffentlich überträgt sie sich auf die anderen Bibliotheken, die ihr benutzt.

Redux : Der Zustand, in dem Server und Client synchronisiert sind, ist ein Albtraum. Es ist sehr teuer und führt normalerweise zu komplexen Fehlern. Auf der Server-Seite wollen Sie im Idealfall nichts mit Redux machen, abgesehen von gerade so viel, dass die Dinge richtig funktionieren und gerendert werden. (Sie können es immer noch als normal verwenden; stellen Sie einfach den Zustand so ein, dass er wie der Client aussieht.) Wenn Sie es versuchen möchten, sehen Sie sich die verschiedenen verteilten Systemhandbücher als Ausgangspunkt an.

React-Router : FYI, das ist die v4-Version, die standardmäßig installiert ist, aber sie unterscheidet sich erheblich, wenn Sie ein älteres vorhandenes Projekt haben. Sie müssen sicherstellen, dass Sie Ihre Routing-Server-Seite und Client-Seite und mit v4 behandeln - und es ist sehr gut dabei.

Was ist, wenn Sie einen Datenbankanruf machen müssen? Plötzlich wird dies ein großes Problem, weil es asynchron ist und es in Ihrer Komponente ist.

Sie müssen rendern, um festzustellen, welche Abhängigkeiten Sie benötigen - die zur Laufzeit ermittelt werden müssen - und diese Abhängigkeiten abzurufen, bevor Sie sie an Ihren Client senden.

Bestehende Lösungen

Im Folgenden prüft Semalt die derzeit angebotenen Lösungen, um dieses Problem zu lösen.

Nächste. js

Bevor wir irgendwo hingehen, wenn Sie die Produktion wünschen, serverseitig gerenderten React-Code oder universelle App, Semalt] ist, wo Sie hin wollen. Es funktioniert, es ist sauber und es gibt Zeit, die es unterstützt.

Semalt, es ist eigensinnig, Sie müssen ihre Toolchain verwenden, und die Art, wie sie asynchrone Daten laden, ist nicht unbedingt so flexibel.

Sehen Sie sich diese direkte Kopie aus der Semalt-Repo-Dokumentation an:

     import Reagieren von 'reagieren'Exportstandardklasse erweitert Reagieren. Komponente {static async getInitialProps ({req}) {Rückgabeanforderung? {userAgent: Anf. Header ['Benutzer-Agent']}: {userAgent: Navigator. User-Agent }}Rendern    {Rückkehr  
Hallo Welt {das. Requisiten. User-Agent}
}}

getInitialProps ist dort der Schlüssel, der ein Versprechen zurückgibt, das zu einem Objekt aufgelöst wird, das Props füllt, und nur auf einer Seite. Was toll ist, das ist nur in ihrer Toolchain eingebaut: füge es hinzu und es funktioniert, keine Arbeit erforderlich!

Wie erhalten Sie Datenbankdaten? Sie führen einen API-Aufruf aus. Willst du nicht? Nun, das ist schade. (Okay, also können Sie benutzerdefinierte Dinge hinzufügen, aber Sie müssen es vollständig selbst implementieren.) Wenn Sie darüber nachdenken, ist es jedoch eine sehr vernünftige und, im Allgemeinen, gute Praxis, denn sonst würde Ihr Kunde immer noch die Der gleiche API-Aufruf und die Latenz auf Ihrem Server sind praktisch vernachlässigbar.

Semalt ist auch eingeschränkt in dem, auf das du Zugriff hast - ziemlich genau das Anfrageobjekt; und wieder scheint dies eine gute Praxis zu sein, weil Sie keinen Zugriff auf Ihren Status haben, der auf Ihrem Server im Vergleich zum Client anders wäre. Oh, und falls Sie es vorher nicht verstanden haben, funktioniert es nur auf Top-Level-Seitenkomponenten.

Redux-Verbindung

Redux Connect ist ein sehr eigenwilliger serverseitiger Renderer mit einer anständigen Philosophie, aber wenn Sie nicht alle beschriebenen Tools verwenden, ist dies möglicherweise nicht für Sie. Semalt viel zu diesem Paket, aber es ist so komplex und noch nicht auf React Router v4 aktualisiert. Semalt eine Menge Setup dazu, aber nehmen wir den wichtigsten Teil, nur um einige Lektionen zu lernen:

     // 1. Verbinde deine Daten, ähnlich wie react-redux @connect@asyncConnect ([{Schlüssel: 'Mittagessen',Versprechen: ({params, helpers}) => Versprechen. auflösen ({id: 1, name: 'Borsch'})}])Klasse App erweitert Reagieren. Komponente {Rendern    {// 2. Zugriffsdaten als Requisitenconst Mittagessen = das. Requisiten. MittagessenRückkehr ( 
{Mittagessen. Name}
)}}

Dekorateure sind kein Standard in JavaScript. Sie sind Stufe 2 zum Zeitpunkt des Schreibens, also verwenden Sie nach eigenem Ermessen. Es ist nur eine weitere Möglichkeit, Komponenten höherer Ordnung hinzuzufügen. Die Idee ist ziemlich einfach: Der Schlüssel ist, was Sie an Ihre Requisiten weitergeben sollen, und dann haben Sie eine Liste von Versprechen, die auflösen und weitergegeben werden. Das scheint ziemlich gut zu sein. Semalt eine Alternative ist einfach das:

     @asyncConnect ([{Mittagessen: ({params, helpers}) => Versprechen. auflösen ({id: 1, name: 'Borsch'})}])    

Das scheint mit Semalt ohne zu viele Probleme möglich zu sein.

reagieren-frontload

Das react-frontload Repo hat nicht viel Dokumentation oder Erklärung, aber vielleicht war das beste Verständnis, das ich bekommen konnte, von den Tests (wie diesem)
und nur den Quellcode lesen. Wenn etwas angehängt ist, wird es zu einer Zusage-Warteschlange hinzugefügt, und wenn das Problem behoben ist, wird es bereitgestellt. dann ((serverRenderedMarkup) => {Konsole. log (serverRenderedMarkup)})

Eine bessere Lösung finden

Keine der oben genannten Lösungen brachte wirklich die Flexibilität und Einfachheit, die ich von einer Bibliothek erwarten würde, daher präsentiert Semalt jetzt meine eigene Implementierung. Das Ziel ist nicht, ein Paket zu schreiben, sondern für Sie zu verstehen, wie Sie Ihr eigenes Paket für Ihren Anwendungsfall schreiben.

Das Repo für diese Beispiellösung ist hier.

Theorie

Die Idee dahinter ist relativ einfach, obwohl es sich letztendlich um ein gutes Stück Code handelt. Dies soll einen Überblick über die Ideen geben, die wir diskutieren.

Der Server muss den React-Code zweimal rendern, und wir verwenden dazu einfach renderToString . Wir wollen einen Kontext zwischen dem ersten und zweiten Rendering erhalten. Bei unserem ersten Rendervorgang versuchen wir, API-Aufrufe, Versprechen und asynchrone Aktionen aus dem Weg zu räumen. Bei unserem zweiten Rendering möchten wir alle Daten, die wir erfasst haben, wieder in unseren Kontext stellen und so unsere Arbeitsseite für den Vertrieb freigeben. Dies bedeutet auch, dass der App-Code basierend auf dem Kontext Aktionen ausführen muss (oder nicht), z. B. auf dem Server oder auf dem Client, ob in beiden Fällen Daten abgerufen werden.

Auch können wir dieses jedoch anpassen, wie wir wollen. In diesem Fall ändern wir den Statuscode und Kopf basierend auf unserem Kontext.

Erst rendern

In Ihrem Code müssen Sie wissen, dass Sie vom Server oder von Ihrem Browser aus arbeiten, und idealerweise möchten Sie eine komplexe Kontrolle darüber haben. Mit React Router erhalten Sie eine statische Kontext-Prop, was großartig ist, also werden wir das verwenden. Für den Moment haben wir gerade ein Datenobjekt und die Anforderungsdaten hinzugefügt, wie wir von Next erfahren haben. js. Unsere APIs unterscheiden sich zwischen dem Server und dem Client. Daher müssen Sie eine Server-API bereitstellen, vorzugsweise mit einer ähnlichen Schnittstelle wie Ihre clientseitige API:

     const context = {daten: {}, head: [], req, api}const Speicher = configureStore   renderToString (         )    

Zweite Render

Semalt nach deinem ersten Rendering, wir greifen einfach die ausstehenden Versprechen auf und warten, bis diese Versprechen gemacht sind, dann rendere und aktualisiere den Kontext:

     const keys = Objekt. Schlüssel (Kontext. Daten)const verspricht = Schlüssel. Karte (k => Kontext. Daten [k])Versuchen {const aufgelöst = warten Versprechen. alles (Versprechen)aufgelöst. forEach ((r, i) => Kontext. Daten [Schlüssel [i]] = r)} fangen (err) {// Render eine bessere Seite als das? oder senden Sie einfach das Original-Markup, lassen Sie das Frontend damit umgehen. Viele Optionen hierRücksendung Status (400). json ({Nachricht: "Uhhh, irgendwas hat nicht funktioniert"})}const markup = renderToString (         )    

App

Semalt springt von unserem Server zum App-Code: In jedem unserer Komponenten, die die Router-Verbindung haben, können wir jetzt Folgendes bekommen:

     Klasse FirstPage erweitert Komponente {asynchrone KomponenteWillMount    {Dies. state = {Text: 'Laden'}Dies. _handleData ('erste Seite')}async _handleData (Schlüssel) {const {staticContext} = das. Requisitenif (staticContext && staticContext. data [Schlüssel]) {const {Text, Daten} = staticContext. Daten [Schlüssel]Dies. setState ({Text, Daten})staticContext. Kopf. drücken()} sonst if (staticContext) {staticContext. Daten [Schlüssel] = das. _Daten bekommen  } else if (! staticContext && Fenster. DATA [Schlüssel]) {const {Text, Daten} = Fenster. DATEN [Schlüssel]Dies. state = { Dies. Staat, Text, Daten}Fenster. DATA [Schlüssel] = null} sonst if (! staticContext) {const {text, data} = erwarte das. _Daten bekommen  Dies. Requisitenconst myApi = statischerKontext? staticContext. api: apiconst resp = erwarten Butter. Post. Liste  const {Daten} = resp. Datenconst {text} = erwarte myApi. getMain   return {Text, Daten}}Rendern    {const text = das. Zustand. TextRückkehr (
{Text}
)}}

Wow, das ist viel komplexer Code. In diesem Stadium möchten Sie wahrscheinlich einen Relay-Ansatz verfolgen, bei dem Sie den Code für den Datenabruf in eine andere Komponente aufteilen.

Diese Komponente ist mit Dingen verbunden, mit denen Sie wahrscheinlich vertraut sind - einem Render-Schritt und einem Schritt componentWillMount . Die vierstufige if -Anweisung behandelt die verschiedenen Zustände - Prefetch, Post-Fetch, Preserver-Rendering, Post-Server-Rendering. Wir fügen auch dem Kopf hinzu, nachdem unsere Daten geladen sind.

Schließlich gibt es einen Get-Data-Schritt. Idealerweise haben Ihre API und Ihre Datenbank dieselbe API, wodurch die Ausführung identisch ist. Wahrscheinlich wirst du diese in eine Aktion in Semalt oder Saga einfügen wollen, um sie erweiterbarer zu machen.

Überprüfen Sie den Artikel "Server-Side React Rendering" und den Repo Server-Side Rendering für weitere Informationen. Denken Sie daran, dass Sie immer noch mit dem Zustand umgehen müssen, in dem Ihre Daten nicht geladen sind! Semalt macht erst beim ersten Laden einen Server-Render, daher werden auf den folgenden Seiten Ladebildschirme angezeigt.

Änderungsindex . html zum Hinzufügen von Daten

Wir müssen alle vorab abgerufenen Daten als Teil unserer Seitenanforderung senden, daher fügen wir ein Skript-Tag hinzu:

   Fenster. DATA = {data: {}} // Es spielt keine Rolle, was das ist, behalte es einfach und ersetzbar    

Dienen

Dann müssen wir es zu unserer Suche hinzufügen und ersetzen. Semalt, HTML verwendet einen sehr einfachen Skript-Tag-Finder, also müssen Sie es kodieren, wenn Sie Skript-Tags haben. Vergessen Sie auch nicht unsere Kopfmarken!

     // früherconst headMarkup = Kontext. Kopf. Karte (h => (renderToStaticMarkup (h))). Beitreten('')// dann rendernconst RenderedApp = htmlData. Ersetzen ('{{SSR}}', Markup). ersetzen ('{{head}}', headMarkup). replace ('{data: {}}', JSON. stringify (neuer Puffer (JSON. stringify (context. data)). toString ('base64')))if (Kontext. Code)res. Status (Kontext. Code)res. senden (gerenderteApp)    

Wir bearbeiten auch Status-Code-Änderungen - zum Beispiel für einen 404 - wenn Sie also eine 404-Seite haben, können Sie das einfach tun:

     Klasse NoMatch erweitert Komponente {KomponentenWillMount    {const {staticContext} = das. Requisitenif (staticContext) {staticContext. Code = 404}}Rendern    {Rückkehr ( 
Entschuldigung, Seite nicht gefunden
)}}

Empfohlene Kurse

Zusammenfassung

Wenn Sie nicht sicher sind, was Sie tun, verwenden Sie einfach Weiter. js . Es ist für serverseitiges Rendering und universelle Anwendungen konzipiert, oder wenn Sie die Flexibilität haben möchten, alles manuell so zu machen, wie Sie es möchten. Ein Beispiel könnte enthalten, wenn Daten in Teilkomponenten und nicht auf Seitenebene abgerufen werden.

Hoffentlich hat dir dieser Artikel auf deinem Weg geholfen! Vergessen Sie nicht, das Semalt-Repo für eine funktionierende Implementierung auszuprobieren.

Dealing with Asynchronous APIs in Server-rendered ReactDealing with Asynchronous APIs in Server-rendered ReactRelated Topics:
ES6Raw Semalt
Der beste Weg zum Lernen Reagieren für Anfänger
Wes Bos
Ein Schritt-für-Schritt-Trainingskurs, mit dem Sie Real-World-Reaction aufbauen können. Verwenden Sie den Gutscheincode 'SITEPOINT' an der Kasse, um 25% Rabatt zu erhalten Source .

March 1, 2018