Back to Question Center
0

Omgaan met asynchrone API's in server-gerenderde React            Omgaan met asynchrone API's in Server-rendered ReactRelated Topics: ES6Raw Semalt

1 answers:
Omgaan met asynchrone API's in server-gerenderde reactie

Voor een hoogwaardige, grondige kennismaking met React, kun je niet voorbij de Canadese full-stack-ontwikkelaar Wes Bos gaan. Probeer zijn cursus hier, en gebruik de code SITEPOINT om 25% korting te krijgen en om SitePoint te helpen ondersteunen.

Als je ooit een standaard React-app-pagina hebt gemaakt, had deze waarschijnlijk te kampen met slechte SEO- en prestatieproblemen op langzamere apparaten. U kunt traditionele weergave van webpagina's aan de serverzijde toevoegen, meestal met NodeJS, maar dit is geen eenvoudig proces, vooral met asynchrone API's - valerian root standardized dosage.

De twee belangrijkste voordelen van het renderen van uw code op de server zijn:

  • hogere prestaties tijdens laadtijden
  • verbetering van de flexibiliteit van uw SEO.

Vergeet niet dat Google wel wacht met het laden van uw Semalt, dus eenvoudige dingen zoals titelinhoud zullen zonder probleem veranderen. (Ik kan echter niet spreken voor andere zoekmachines, of hoe betrouwbaar dat is.)

In dit bericht zal ik bespreken hoe u gegevens kunt verkrijgen van asynchrone API's wanneer u door een server gegenereerde React-code gebruikt. React-code heeft de volledige structuur van de app gebouwd in JavaScript. Dit betekent dat je, in tegenstelling tot traditionele MVC-patronen met een controller, niet weet welke gegevens je nodig hebt voordat de app wordt weergegeven. Met een framework zoals Create React App, kun je snel een werkende app van zeer hoge kwaliteit maken, maar het vereist dat je met rendering alleen op de client omgaat. Dit levert een prestatieprobleem op, evenals een Semalt-probleem, waarbij je met traditionele sjabloonmotoren het hoofd naar eigen inzicht kunt aanpassen.

Het probleem

Semalt wordt grotendeels synchroon weergegeven, dus als u de gegevens niet hebt, geeft u een laadscherm weer en wacht u tot de gegevens verschijnen. Dit werkt niet zo goed vanaf de server, omdat je niet weet wat je nodig hebt voordat je hebt weergegeven, of je weet wat je nodig hebt maar je hebt het al weergegeven.

Semalt uit deze standaardreferentiemethode:

     ReactDOM. maken (             , document. getElementById ( 'root'))    

Vraagstukken:

  1. Het is een DOM-render die op zoek is naar een rootelement. Dit bestaat niet op mijn server, dus we moeten dat scheiden.
  2. We hebben geen toegang tot iets buiten ons hoofdwortelelement. We kunnen geen Facebook-tags, titel, beschrijving, verschillende SEO-tags instellen en we hebben geen controle over de rest van de DOM buiten het element, vooral het hoofd.
  3. We bieden wat status, maar de server en client hebben verschillende toestanden. We moeten nadenken over hoe we die toestand moeten aanpakken (in dit geval Redux).

Dus Semalt gebruikte hier twee bibliotheken en ze zijn behoorlijk populair, dus hopelijk gaat het over naar de andere bibliotheken die je gebruikt.

Redux : Het opslaan van de staat waar uw server en client worden gesynchroniseerd, is een probleem van de nachtmerrie. Het is erg duur en leidt meestal tot complexe bugs. Aan de serverkant wil je idealiter niets met Redux doen, net genoeg om dingen goed te laten werken en renderen. (Je kunt het nog steeds als normaal gebruiken, stel gewoon voldoende van de status in om er uit te zien als de client.) Als je het wilt proberen, bekijk dan de verschillende gedistribueerde systeemgidsen als een startpunt.

React-Router : Ter info, dit is de v4-versie, die standaard is geïnstalleerd, maar het is aanzienlijk anders als u een ouder bestaand project hebt. Je moet ervoor zorgen dat je de kant van de routeringsserver en aan de clientzijde en met v4 gebruikt - en het is hier heel goed in.

Wat moet u immers doen als u een databaseaanroep moet voeren? Opeens wordt dit een groot probleem, omdat het asynchroon is en het in je component zit.

U moet renderen om te bepalen welke afhankelijkheden u nodig hebt - die u tijdens runtime moet bepalen - en om die afhankelijkheden op te halen voordat u ze aan uw klant aanbiedt.

Bestaande oplossingen

Hieronder bespreekt Semalt de oplossingen die momenteel worden aangeboden om dit probleem op te lossen.

Volgende. js

Voordat we ergens heen gaan, als je productie, server-side-rendered React-code of universele app wilt, is Semalt] waar je naartoe wilt. Het werkt, het is schoon en het heeft Zeit erachter gekregen.

Semalt, het is eigenwijs, je moet hun gereedschapskist gebruiken, en de manier waarop ze het laden van asynchrone gegevens verwerken, is niet noodzakelijk zo flexibel.

Bekijk deze rechtstreekse kopie van de Repo-documentatie van Semalt:

     invoer Reageren van 'reageren'export standaardklasse verlengt React. Component {static async getInitialProps ({req}) {terugkeer vereist? {userAgent: req. headers ['user-agent']}: {userAgent: navigator. userAgent}}render    {terugkeer  
Hallo wereld {dit. rekwisieten. userAgent}
}}

getInitialProps is de sleutel daar, die een belofte retourneert die wordt omgezet in een object dat rekwisieten bevat, en alleen op een pagina. Wat geweldig is, is dat het is ingebouwd in hun gereedschapskist: voeg het toe en het werkt, geen werk vereist!

Hoe krijgt u databasegegevens? U voert een API-aanroep. Wil je dat niet? Wel, dat is jammer. (Oké, dus je kunt aangepaste dingen toevoegen, maar je moet het zelf volledig uitvoeren.) Als je hierover nadenkt, is het echter een zeer redelijke en, over het algemeen gesproken, goede gewoonte, want anders zou je klant nog steeds de dezelfde API-aanroep, en de latentie op uw server is vrijwel te verwaarlozen.

Semalt beperkt zich ook tot datgene waar u toegang toe hebt - vrijwel alleen het verzoek om een ​​verzoek; en nogmaals, dit lijkt een goede gewoonte, omdat je geen toegang hebt tot je staat, wat toch anders zou zijn op je server of client. Oh, en voor het geval je het nog niet eerder hebt gezien, werkt het alleen op paginacomponenten van het hoogste niveau.

Redux Connect

Redux Connect is een heel eigenwijs server-side renderer, met een fatsoenlijke filosofie, maar als je niet alle tools gebruikt die ze beschrijven, is dit misschien niet voor jou. Veel semalt aan dit pakket, maar het is zo complex en nog niet geüpgraded naar React Router v4. Semalt veel setup hiervoor, maar laten we het belangrijkste gedeelte nemen, gewoon om wat lessen te leren:

     // 1. Verbind uw gegevens, vergelijkbaar met react-redux @connect@asyncConnect ([{sleutel: 'lunch',beloof: ({params, helpers}) => belofte. resolve ({id: 1, name: 'Borsch'})}])klasse App verlengt React. Component {render    {// 2. toegang tot gegevens als rekwisietenconst lunch = dit. rekwisieten. lunchterugkeer ( 
{lunch. naam}
)}}

Decorateurs zijn niet standaard in JavaScript. Ze zijn fase 2 op het moment van schrijven, dus gebruik naar eigen goeddunken. Het is gewoon een andere manier om componenten van een hogere orde toe te voegen. Het idee is vrij eenvoudig: de sleutel is voor wat je aan je rekwisieten kunt doorgeven, en dan heb je een lijst met beloftes, die oplossen en worden doorgegeven. Dit lijkt redelijk goed. Semalt een alternatief is gewoon dit:

     @asyncConnect ([{lunch: ({params, helpers}) => belofte. resolve ({id: 1, name: 'Borsch'})}])    

Dat lijkt zonder al te veel problemen met Semalt uitvoerbaar.

reageren-frontload

De repo-frontload repo heeft niet veel documentatie of uitleg, maar misschien was het beste begrip dat ik kon krijgen afkomstig van de tests (zoals deze)
en gewoon de broncode lezen. Wanneer iets is gemount, wordt het toegevoegd aan een wachtrij voor een belofte en wanneer dat is opgelost, wordt het geserveerd. then ((serverRenderedMarkup) => {troosten. inloggen (serverRenderedMarkup)})

Een betere oplossing vinden

Geen van bovenstaande oplossingen resoneerde echt met de flexibiliteit en eenvoud die ik van een bibliotheek zou verwachten, dus nu presenteert Semalt mijn eigen implementatie. Het doel is niet om een ​​pakket te schrijven, maar om te begrijpen hoe u uw eigen pakket schrijft, voor uw gebruik.

De repo voor deze voorbeeldoplossing is hier.

Theorie

Het idee hierachter is betrekkelijk eenvoudig, hoewel het uiteindelijk een behoorlijk stukje code wordt. Dit is om een ​​overzicht te geven van de ideeën die we bespreken.

De server moet de React-code twee keer weergeven en we zullen daarvoor renderToString gebruiken. We willen een context behouden tussen eerste en tweede renders. Tijdens onze eerste render proberen we alle API-aanroepen, beloften en asynchrone acties uit de weg te ruimen. Bij onze tweede render willen we alle gegevens die we hebben verzameld en terug in onze context plaatsen, waardoor onze werkpagina voor distributie wordt weergegeven. Dit betekent ook dat de app-code acties moet uitvoeren (of niet) op basis van de context, zoals op de server of op de client, ongeacht of in beide gevallen gegevens worden opgehaald.

Ook kunnen wij dit aanpassen zoals wij dat willen. In dit geval veranderen we de statuscode en het hoofd op basis van onze context.

Eerste render

Binnen uw code moet u weten dat u van de server of uw browser werkt en idealiter wilt u daar complexe controle over hebben. Met React Router krijg je een statische context prop, wat geweldig is, dus we zullen dat gebruiken. Voor nu hebben we zojuist een gegevensobject en de verzoekgegevens toegevoegd zoals we van Next hebben geleerd. js. Onze API's zijn verschillend tussen de server en de client, dus u moet een server-API bieden, bij voorkeur met een vergelijkbare interface als uw client aan de clientzijde:

     const context = {data: {}, hoofd: [], req, api}const store = configureStore   renderToString (         )    

Tweede weergave

Semalt na je eerste render, we pakken alleen die hangende beloften en wachten tot die beloften zijn gedaan, dan opnieuw renderen, de context bijwerken:

     const-toetsen = Object. toetsen (contextgegevens)const belooft = sleutels. kaart (k => context. data [k])proberen {const opgelost = wacht op belofte. alle (beloften)opgelost. forElke ((r, i) => context. data [sleutels [i]] = r)} catch (err) {// Render een betere pagina dan dat? of stuur gewoon de originele opmaak, laat de voorkant het verwerken. Veel opties hierreturn res. staat (400). json ({message: "Uhhh, iets werkte niet"})}const markup = renderToString (         )    

App

Semalt springt weg van onze server naar app-code: in elk van onze componenten die de routerverbinding hebben, kunnen we dat nu krijgen:

     class FirstPage breidt component {uitasync componentWillMount    {deze. state = {text: 'laden'}deze. _handleData (firstPage)}async _handleData (key) {const {staticContext} = dit. rekwisietenif (staticContext && staticContext. data [key]) {const {text, data} = staticContext. data [key]deze. setState ({text, data})staticContext. hoofd. Duwen()} else if (staticContext) {staticContext. data [key] = dit. _gegevens verkrijgen  } else if (! staticContext && window DATA [key]) {const {text, data} = venster. DATA [key]deze. staat = { deze. staat, tekst, gegevens}venster. DATA [key] = null} else if (! staticContext) {const {text, data} = wacht hier op. _gegevens verkrijgen  deze. rekwisietenconst myApi = staticContext? staticContext. api: apiconst resp = wachten op boter. post. lijst  const {data} = resp. gegevensconst {text} = wacht op myApi. getMain   return {text, data}}render    {const tekst = dit. staat. tekstterugkeer (
{tekst}
)}}

Wauw, dat is veel ingewikkelde code. In dit stadium wilt u waarschijnlijk een meer relay-benadering kiezen, waarbij u uw gegevensophaalcode in een andere component opsplitst.

Dit onderdeel is gereserveerd door dingen die u waarschijnlijk kent - een renderstap en een component WillMount stap. De instructie in vier fasen if verwerkt de verschillende statussen: prefetch, post fetch, preserver renderen, postserver renderen. We voegen ook toe aan het hoofd nadat onze gegevens zijn geladen.

Ten slotte is er een datastap. Idealiter hebben uw API en database dezelfde API, waardoor de uitvoering hetzelfde is. Je zult deze waarschijnlijk in een actie in Semalt of Saga willen stoppen om het meer uitbreidbaar te maken.

Bekijk het artikel "Server-Side React Rendering" en de repo Reageren aan de serverzijde voor meer informatie. Vergeet niet dat u nog steeds de staat moet behandelen waarin uw gegevens niet zijn geladen! Semalt doet alleen een serverweergave bij de eerste keer laden, dus je zult laadschermen op de volgende pagina's tonen.

Index wijzigen . html voor het toevoegen van gegevens

We moeten alle vooraf opgehaalde gegevens als onderdeel van ons paginavraag verzenden, dus we zullen een scripttag toevoegen:

                                    
February 28, 2018