Πώς να εφαρμόσετε την απόδοση από διακομιστή στην εφαρμογή React σε τρία απλά βήματα

Από τον Rohit Kumar

Εδώ θα δημιουργήσουμε αυτό το σεμινάριο: μια ωραία κάρτα React σαν αυτή.

Σε αυτό το σεμινάριο, θα χρησιμοποιήσουμε την απόδοση από την πλευρά του διακομιστή για να παραδώσουμε μια απόκριση HTML όταν ένας χρήστης ή πρόγραμμα ανίχνευσης φτάσει σε μια διεύθυνση URL σελίδας. Θα χειριστούμε τα τελευταία αιτήματα από την πλευρά του πελάτη.

Γιατί το χρειαζόμαστε;

Επιτρέψτε μου να σας καθοδηγήσω στην απάντηση.

Ποια είναι η διαφορά μεταξύ απόδοσης από πλευράς πελάτη και απόδοσης από διακομιστή;

Στην απόδοση από πλευράς πελάτη, το πρόγραμμα περιήγησής σας κατεβάζει μια ελάχιστη σελίδα HTML. Αποδίδει τη JavaScript και συμπληρώνει το περιεχόμενο σε αυτό.

Η απόδοση από την πλευρά του διακομιστή , από την άλλη πλευρά, αποδίδει τα στοιχεία React στον διακομιστή. Το αποτέλεσμα είναι περιεχόμενο HTML.

Μπορείτε να συνδυάσετε αυτά τα δύο για να δημιουργήσετε μια ισόμορφη εφαρμογή.

Μειονεκτήματα του Rendering React στον διακομιστή

  • Το SSR μπορεί να βελτιώσει την απόδοση εάν η εφαρμογή σας είναι μικρή. Αλλά μπορεί επίσης να υποβαθμίσει την απόδοση εάν είναι βαρύ.
  • Αυξάνει τον χρόνο απόκρισης (και μπορεί να είναι χειρότερο εάν ο διακομιστής είναι απασχολημένος).
  • Αυξάνει το μέγεθος της απόκρισης, πράγμα που σημαίνει ότι η φόρτωση της σελίδας χρειάζεται περισσότερο χρόνο.
  • Αυξάνει την πολυπλοκότητα της εφαρμογής.

Πότε πρέπει να χρησιμοποιείτε την Απόδοση από πλευράς διακομιστή;

Παρά αυτές τις συνέπειες του SSR, υπάρχουν ορισμένες καταστάσεις στις οποίες μπορείτε και πρέπει να το χρησιμοποιήσετε.

1. SEO

Κάθε ιστότοπος θέλει να εμφανίζεται σε αναζητήσεις. Διόρθωσε με αν κάνω λάθος.

Δυστυχώς, τα προγράμματα ανίχνευσης μηχανών αναζήτησης δεν καταλαβαίνουν ακόμη / αποδίδουν JavaScript.

Αυτό σημαίνει ότι βλέπουν μια κενή σελίδα, ανεξάρτητα από το πόσο χρήσιμος είναι ο ιστότοπός σας.

Πολλοί άνθρωποι λένε ότι το πρόγραμμα ανίχνευσης της Google δίνει τώρα JavaScript.

Για να το δοκιμάσω, ανέπτυξα την εφαρμογή στο Heroku. Εδώ είναι αυτό που είδα στο Google Search Console:

Μια κενή σελίδα.

Αυτός ήταν ο μεγαλύτερος λόγος που εξερεύνησα την απόδοση του διακομιστή. Ειδικά όταν πρόκειται για μια ακρογωνιαία σελίδα όπως μια σελίδα προορισμού, ένα blog και ούτω καθεξής.

Για να επαληθεύσετε εάν η Google κάνει τον ιστότοπό σας, επισκεφθείτε:

Πίνακας ελέγχου Search Console> Ανίχνευση> Ανάκτηση ως Google. Εισαγάγετε τη διεύθυνση URL της σελίδας ή αφήστε την κενή για την αρχική σελίδα.

Επιλέξτε FETCH AND RENDER. Μόλις ολοκληρωθεί, κάντε κλικ για να δείτε το αποτέλεσμα.

2. Βελτιώστε την απόδοση

Στο SSR, η απόδοση της εφαρμογής εξαρτάται από τους πόρους του διακομιστή και την ταχύτητα δικτύου του χρήστη. Αυτό το καθιστά πολύ χρήσιμο για ιστότοπους με μεγάλο περιεχόμενο.

Για παράδειγμα , ας πούμε ότι έχετε ένα κινητό τηλέφωνο μεσαίας τιμής με αργή ταχύτητα στο Διαδίκτυο. Προσπαθείτε να αποκτήσετε πρόσβαση σε έναν ιστότοπο που κατεβάζει 4MB δεδομένων προτού μπορέσετε να δείτε οτιδήποτε.

Θα μπορούσατε να δείτε οτιδήποτε στην οθόνη σας εντός 2–4 δευτερολέπτων;

Θα επισκεφθείτε ξανά αυτόν τον ιστότοπο;

Δεν νομίζω ότι θα το κάνατε.

Μια άλλη σημαντική βελτίωση είναι στον πρώτο χρόνο αλληλεπίδρασης χρήστη. Αυτή είναι η διαφορά στο χρόνο από όταν ένας χρήστης χτυπά τη διεύθυνση URL έως όταν βλέπει περιεχόμενο.

Εδώ είναι η σύγκριση. Το δοκίμασα σε Mac ανάπτυξης.

React Rendered στον διακομιστή

Ο πρώτος χρόνος αλληλεπίδρασης είναι 300ms. Το Hydrate τελειώνει στα 400ms. Το συμβάν φόρτωσης εξέρχεται στα 500ms περίπου. Μπορείτε να το δείτε ελέγχοντας την παραπάνω εικόνα.

React Rendered στο πρόγραμμα περιήγησης του πελάτη

Ο πρώτος χρόνος αλληλεπίδρασης είναι 400ms. Το συμβάν φόρτωσης τερματίζεται στα 470ms.

Το αποτέλεσμα μιλάει από μόνο του. Υπάρχει μια διαφορά 100ms στον πρώτο χρόνο αλληλεπίδρασης χρήστη για μια τόσο μικρή εφαρμογή.

Πώς λειτουργεί? - (4 απλά βήματα)

  • Δημιουργήστε ένα νέο κατάστημα Redux σε κάθε αίτημα.
  • Προαιρετικά αποστέλλετε ορισμένες ενέργειες.
  • Αποκτήστε την κατάσταση από το Store και εκτελέστε SSR.
  • Στείλτε την κατάσταση που αποκτήθηκε στο προηγούμενο βήμα μαζί με την απάντηση.

We will use the state passed in the response for creating the initial state on client-side.

Before you get started, clone/download the complete example from Github and use it for reference.

Getting Started by Setting up our App

First, open your favourite editor and shell. Create a new folder for your application. Let’s start.

npm init --yes

Fill in the details. After package.json is created, copy the dependencies and scripts below into it.

Install all dependencies by running:

npm install

You need to configure Babel and webpack for our build script to work.

Babel transforms ESM and react into Node and browser-understood code.

Create a new file .babelrc and put the line below in it.

{ "presets": ["@babel/env", "@babel/react"] } 

webpack bundles our app and its dependencies into a single file. Create another file webpack.config.js with the following code in it:

const path = require('path');module.exports = { entry: { client: './src/client.js', bundle: './src/bundle.js' }, output: { path: path.resolve(__dirname, 'assets'), filename: "[name].js" }, module: { rules: [ { test: /\.js$/, exclude: /node_modules/, loader: "babel-loader" } ] } }

The build process output’s two files:

  1. assets/bundle.js — pure client side app.
  2. assets/client.js — client side companion for SSR.

The src/ folder contains the source code. The Babel compiled files go into views/. views directory will be created automatically if not present.

Why do we need to compile source files?

The reason is the syntax difference between ESM & CommonJS. While writing React and Redux, we heavily use import and export in all files.

Unfortunately, they don’t work in Node. Here comes Babel to rescue. The script below tells Babel to compile all files in the src directory and put the result in views.

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

Now, Node can run them.

Copy Precoded & Static files

If you have already cloned the repository, copy from it. Otherwise download ssr-static.zip file from Dropbox. Extract it and keep these three folders inside your app directory. Here’s what they contain.

  1. React App and components resides in src/components.
  2. Redux files in src/redux/.
  3. assets/ & media/: Contain static files such as style.css and images.

Server Side

Create two new files named server.js and template.js inside the src/ folder.

1. src/server.js

Magic happens here. This is the code you’ve been searching for.

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(); const preloadedState = store.getState(); return { content, preloadedState }; };

Instead of rendering our app, we need to wrap it into a function and export it. The function accepts the initial state of the application.

Here’s how it works.

  1. Pass initialState to configureStore(). configureStore()returns a new Store instance. Hold it inside the store variable.
  2. Call renderToString() method, providing our App as input. It renders our app on the server and returns the HTML produced. Now, the variable content stores the HTML.
  3. Get the state out of Redux Store by calling getState() on store. Keep it in a variable preloadedState.
  4. Return the content and preloadedState. We will pass these to our template to get the final HTML page.

2. src/template.js

template.js exports a function. It takes title, state and content as input. It injects them into the template and returns the final HTML document.

To pass along the state, the template attaches state to window.__STATE__ inside a pt> tag.

Now you can read state on the client side by accessing window.__STATE__.

We also include the SSR companion assets/client.js client-side application in another script tag.

If you request the pure client version, it only puts assets/bundle.js inside the script tag.

The Client Side

The client side is pretty straightforward.

1. src/bundle.js

This is how you write the React and Redux Provider wrap. It is our pure client-side app. No tricks here.

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( , document.querySelector('#app') );

2. src/client.js

Looks familiar? Yeah, there is nothing special except window.__STATE__. All we need to do is grab the initial state from window.__STATE__ and pass it to our configureStore() function as the initial state.

Let’s take a look at our new client file:

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( , document.querySelector('#app') );

Let’s review the changes:

  1. Replace render() with hydrate(). hydrate() is the same as render() but is used to hydrate elements rendered by ReactDOMServer. It ensures that the content is the same on the server and the client.
  2. Read the state from the global window object window.__STATE__. Store it in a variable and delete the window.__STATE__.
  3. Create a fresh store with state as initialState.

All done here.

Putting it all together

Index.js

This is the entry point of our application. It handles requests and templating.

It also declares an initialState variable. I have modelled it with data in the assets/data.json file. We will pass it to our ssr() function.

Note: While referencing a file that is inside src/ from a file outside src/ , use normal require() and replace src/ by views/. You know the reason (Babel compile).

Routing

  1. /: By default server-rendered homepage.
  2. /client: Pure client-side rendering example.
  3. /exit: Server stop button. Only available in development.

Build & Run

It’s time to build and run our application. We can do this with a single line of code.

npm run build && npm run start

Now, the application is running at //localhost:3000.

Ready to become a React Pro?

I am starting a new series from next Monday to get your React skills blazing, immediately.

Thank you for reading this.

If you like it and find it useful, follow me on Twitter & Webflow.