Så här implementerar du server-side rendering i din React-app i tre enkla steg

av Rohit Kumar

Det här är vad vi kommer att bygga i den här handledningen: ett snyggt React-kort som det här.

I den här handledningen använder vi server-side rendering för att leverera ett HTML-svar när en användare eller en crawler klickar på en webbadress. Vi hanterar de senare förfrågningarna på klientsidan.

Varför behöver vi det?

Låt mig guida dig till svaret.

Vad är skillnaden mellan rendering på klientsidan och rendering på serversidan?

I rendering på klientsidan laddar din webbläsare ner en minimal HTML-sida. Den render JavaScript och fyller innehållet i den.

Server-side rendering å andra sidan render React-komponenterna på servern. Utgången är HTML-innehåll.

Du kan kombinera dessa två för att skapa en isomorf app.

Konsekvenser av att rendera React på servern

  • SSR kan förbättra prestandan om din applikation är liten. Men det kan också försämra prestandan om den är tung.
  • Det ökar svarstiden (och det kan bli värre om servern är upptagen).
  • Det ökar svarsstorleken, vilket innebär att sidan tar längre tid att ladda.
  • Det ökar applikationens komplexitet.

När ska du använda Server Side Rendering?

Trots dessa konsekvenser av SSR finns det vissa situationer där du kan och bör använda det.

SEO

Alla webbplatser vill synas i sökningar. Rätta mig om jag har fel.

Tyvärr förstår/renderar sökmotorernas crawlers ännu inte JavaScript.

Detta innebär att de ser en tom sida, oavsett hur hjälpsam din webbplats är.

Många säger att Googles crawler nu renderar JavaScript.

För att testa detta distribuerade jag appen på Heroku. Här är vad jag såg i Google Search Console:

Google’s crawler does not render React

En tom sida.

Det här var den största anledningen till att jag utforskade server-side rendering. Särskilt när det är en hörnstenssida som en landningssida, blogg och så vidare.

För att verifiera om Google renderar din webbplats, besök:

Search Console Dashboard > Crawl > Fetch as Google. Ange sidans URL eller låt den vara tom för hemsidan.

Välj FETCH AND RENDER. När du är klar klickar du på för att se resultatet.

Förbättra prestanda

I SSR beror applikationens prestanda på serverns resurser och användarens nätverkshastighet. Detta gör den mycket användbar för innehållstunga webbplatser.

Till exempel: Säg att du har en mobiltelefon till medelpris med långsam internethastighet. Du försöker komma åt en webbplats som laddar ner 4 MB data innan du kan se något.

Skulle du kunna se något på skärmen inom 2-4 sekunder?

Skulle du besöka den webbplatsen igen?

Jag tror inte att du skulle göra det.

En annan stor förbättring är den första användarinteraktionstiden. Detta är skillnaden i tid från det att en användare trycker på URL:n till dess att de ser innehållet.

Här är jämförelsen. Jag testade den på en utvecklings-Mac.

React Rendered on Server

SSR performance report (Chrome)

Den första interaktionstiden är 300ms. Hydrate avslutas efter 400 ms. Lastningshändelsen avslutas vid 500 ms ungefär. Du kan se detta genom att titta på bilden ovan.

React Rendered on Client’s Browser

Prestandarapport på klientsidan (Chrome)

Den första interaktionstiden är 400ms. Laddningshändelsen avslutas vid 470 ms.

Resultatet talar för sig självt. Det är en skillnad på 100 ms i första användarinteraktionstiden för en så liten app.

Hur fungerar det? – (4 enkla steg)

  • Skapa en ny Redux Store vid varje begäran.
  • Optionellt skicka några åtgärder.
  • Hämta tillståndet från Store och utför SSR.
  • Sänd det tillstånd som erhållits i föregående steg tillsammans med svaret.

Vi kommer att använda tillståndet som skickas i svaret för att skapa det initiala tillståndet på klientsidan.

Innan du börjar, klona/downloadar du det kompletta exemplet från Github och använder det som referens.

Kom igång genom att konfigurera vår app

Först öppnar du din favoritredigerare och shell. Skapa en ny mapp för din applikation. Låt oss börja.

npm init --yes

Fyller ut detaljerna. När package.json har skapats kopierar du nedanstående beroenden och skript till den.

Installera alla beroenden genom att köra:

npm install

Du måste konfigurera Babel och webpack för att vårt byggskript ska fungera.

Babel omvandlar ESM och react till Node- och webbläsarförståelig kod.

Skapa en ny fil .babelrc och lägg in raden nedan i den.

{ "presets": }

webpack paketerar vår app och dess beroenden i en enda fil. Skapa en annan fil webpack.config.js med följande kod i den:

const path = require('path');module.exports = { entry: { client: './src/client.js', bundle: './src/bundle.js' }, output: { path: path.resolve(__dirname, 'assets'), filename: ".js" }, module: { rules: }}

Byggprocessen ger ut två filer:

  1. assets/bundle.js – ren app på klientsidan.
  2. assets/client.js – kompanjon på klientsidan för SSR.

Mappen src/ innehåller källkoden. De Babel-kompilerade filerna hamnar i views/. views-katalogen skapas automatiskt om den inte finns.

Varför behöver vi kompilera källfilerna?

Anledningen är syntaxskillnaden mellan ESM & CommonJS. När vi skriver React och Redux använder vi import och export flitigt i alla filer.

Tyvärr fungerar de inte i Node. Här kommer Babel till undsättning. Skriptet nedan säger till Babel att kompilera alla filer i katalogen src och lägga resultatet i views.

"babel": "babel src -d views",

Nu kan Node köra dem.

Kopiera förkodade & statiska filer

Om du redan har klonat förrådet, kopiera från det. Annars laddar du ner ssr-static.zip-filen från Dropbox. Extrahera den och förvara dessa tre mappar i din app-katalog. Här är vad de innehåller.

  1. React App och komponenter finns i src/components.
  2. Redux-filer i src/redux/.
  3. assets/ & media/: Innehåller statiska filer som style.css och bilder.

Server Side

Skapa två nya filer som heter server.js och template.js i mappen src/.

src/server.js

Trollet sker här. Detta är koden du har letat efter.

import React from 'react';import { renderToString } from 'react-dom/server';import { Provider } from 'react-redux';import configureStore from './redux/configureStore';import App from './components/app';module.exports = function render(initialState) { // Model the initial state const store = configureStore(initialState); let content = renderToString(<Provider store={store} ><App /></Provider>); const preloadedState = store.getState(); return { content, preloadedState };};

Istället för att rendera vår app måste vi packa in den i en funktion och exportera den. Funktionen accepterar applikationens initialtillstånd.

Här är hur det fungerar.

  1. Pass initialState till configureStore(). configureStore()återger en ny Store-instans. Håll den inne i variabeln store.
  2. Anropa metoden renderToString() och ge vår App som indata. Den renderar vår app på servern och returnerar den HTML som produceras. Nu lagrar variabeln content HTML:n.
  3. Hämta tillståndet från Redux Store genom att anropa getState()store. Behåll det i en variabel preloadedState.
  4. Returnerar content och preloadedState. Vi skickar dessa till vår mall för att få den slutliga HTML-sidan.

2. src/template.js

template.js exporterar en funktion. Den tar title, state och content som indata. Den injicerar dem i mallen och returnerar det slutliga HTML-dokumentet.

För att överföra tillståndet fäster mallen state till window.__STATE__ inuti en <script>-tagg.

Nu kan du läsa state på klientsidan genom att få åtkomst till window.__STATE__.

Vi inkluderar också klientsidan för SSR-kamraten assets/client.js i en annan skripttagg.

Om du begär den rena klientversionen lägger den bara assets/bundle.js i skripttaggen.

Klientsidan

Klientsidan är ganska okomplicerad.

src/bundle.js

Det är så här du skriver React- och Redux Provider-omslaget. Det är vår renodlade app på klientsidan. Inga trick här.

import React from 'react';import { render } from 'react-dom';import { Provider } from 'react-redux';import configureStore from './redux/configureStore';import App from './components/app';const store = configureStore();render( <Provider store={store} > <App /> </Provider>, document.querySelector('#app'));

src/client.js

Låter bekant? Ja, det finns inget speciellt förutom window.__STATE__. Allt vi behöver göra är att hämta det initiala tillståndet från window.__STATE__ och skicka det till vår configureStore()-funktion som det initiala tillståndet.

Vi tar en titt på vår nya klientfil:

import React from 'react';import { hydrate } from 'react-dom';import { Provider } from 'react-redux';import configureStore from './redux/configureStore';import App from './components/app';const state = window.__STATE__;delete window.__STATE__;const store = configureStore(state);hydrate( <Provider store={store} > <App /> </Provider>, document.querySelector('#app'));

Vi går igenom ändringarna:

  1. Ersätt render() med hydrate(). hydrate() är samma sak som render() men används för att hydrera element som återges av ReactDOMServer. Det säkerställer att innehållet är detsamma på servern och klienten.
  2. Läs statusen från det globala fönsterobjektet window.__STATE__. Lagra det i en variabel och ta bort window.__STATE__.
  3. Skapa ett nytt lager med state som initialState.

Allt är klart här.

Sätt ihop allt

Index.js

Det här är ingångspunkten för vårt program. Den hanterar förfrågningar och templating.

Den deklarerar också en initialStatevariabel. Jag har modellerat den med data i assets/data.json filen. Vi kommer att skicka den till vår ssr()-funktion.

Notera: När du hänvisar till en fil som är inne i src/ från en fil utanför src/, använd normal require() och ersätt src/ med views/. Du vet varför (Babel-kompilering).

Routing

  1. /: Som standard serverrenderad hemsida.
  2. /client:
  3. /exit: Knapp för serverstopp. Endast tillgänglig i utveckling.

Bygg & Kör

Det är dags att bygga och köra vårt program. Vi kan göra detta med en enda kodrad.

npm run build && npm run start

Nu körs applikationen på http://localhost:3000.

Preparat att bli en React Pro?

Jag startar en ny serie från och med nästa måndag för att få dina React-kunskaper att brinna, omedelbart.

Abonnemangslänken nedan?

Tack för att du läste detta.

Om du gillar det och finner det användbart, följ mig på Twitter & Webflow.

Lämna ett svar

Din e-postadress kommer inte publiceras.