Il meglio di entrambi i mondi: SSR con JavaScript isomorfo

Il rendering lato server, o SSR, è una frase che si sente spesso nella comunità di sviluppo frontend.

Al livello più elementare, il rendering lato server è esattamente ciò che descrive: il rendering delle applicazioni sul server. Si accede a un sito Web, si effettua una richiesta al server, si esegue il rendering di alcuni HTML e si ottiene il risultato completamente reso nel browser. Abbastanza semplice. Potresti chiederti perché la comunità ha anche una parola d’ordine per questo.

Prima dell’ascesa di applicazioni web ricche e dinamiche che si basavano pesantemente su JavaScript e jQuery, essenzialmente tutte le applicazioni Web erano renderizzate dal server. PHP, WordPress, e anche solo siti HTML di base sono esempi di questo.

Quando si visita una pagina su uno di questi siti, si ottiene indietro tutti i dati HTML e tutti. Se si fa clic su un collegamento, il browser effettuerà un’altra richiesta al server. Dopo la risposta, il browser aggiornerà e renderà la pagina successiva da zero. Questo approccio funziona bene, e ha per anni; i browser sono incredibilmente veloci nel rendering HTML statico. Cosa è cambiato?

Dall’inizio del secolo, l’utilizzo di JavaScript è passato da una piccola spruzzata qua e là per l’interattività delle pagine web al linguaggio indiscusso di scelta sul web. Siamo costantemente spedizione più logica e JavaScript per il browser.

Framework a pagina singola come React e Vue hanno inaugurato questa nuova era di applicazioni Web client-renderizzate dinamiche, complesse e basate sui dati. Queste SPA differiscono dalle applicazioni renderizzate dal server perché non recuperano il contenuto completamente renderizzato completo di dati dal server prima del rendering sullo schermo.

Le applicazioni renderizzate lato client rendono il loro contenuto nel browser utilizzando JavaScript. Invece di recuperare tutto il contenuto dal server, semplicemente recuperano una pagina HTML barebone senza contenuto del corpo e rendono tutto il contenuto all’interno utilizzando JavaScript.

Il vantaggio di questo è che si evitano gli aggiornamenti a pagina intera che si verificano con applicazioni completamente renderizzate dal server, il che può essere un po ‘ stridente per l’utente. Le app con rendering client a pagina singola aggiorneranno il contenuto sullo schermo, recupereranno i dati dalle API e si aggiorneranno proprio di fronte a te senza alcun tipo di aggiornamento della pagina. Questa caratteristica è ciò che rende moderne applicazioni web si sentono scattanti e” nativo ” come si interagisce con loro.

Trade-off di rendering lato client

Non è tutto sole e arcobaleni nel mondo SPA reso lato client. Ci sono alcuni compromessi che vengono con il rendering dell’applicazione sul lato client. Due esempi principali sono SEO e prestazioni di carico iniziale.

SEO

Poiché le app renderizzate dai client restituiscono una pagina HTML nuda e cruda con pochissimo contenuto prima che JavaScript entri in gioco e renda il resto, può essere difficile per i crawler dei motori di ricerca comprendere la struttura HTML della tua pagina, che è dannosa per le classifiche di ricerca del tuo sito. Google ha fatto un sacco di buon lavoro intorno a questo, ma è comunque consigliabile evitare il rendering lato client se SEO è particolarmente importante.

caricamento Iniziale delle prestazioni

Con il cliente-reso applicazioni, in genere, vedere le seguenti cose accadono quando si apre la pagina:

  • L’app carichi di alcune conoscenze di base di HTML, come un app shell o statico navbar
  • vedi un indicatore di caricamento di qualche tipo
  • Il contenuto è poi reso

Il problema con questo è che l’applicazione non mostra nulla fino a quando il JavaScript carichi completamente dalla rete ed è finito il rendering di elementi su schermo.

In poche parole, il problema con le prestazioni lato client in generale è che non è possibile controllare quale dispositivo client qualcuno utilizza l’applicazione su-che si tratti di loro state — of-the-art smartphone, potente macchina desktop di fascia alta, o smartphone 100 smartphone di fascia bassa.

Noi, tuttavia, controlliamo il server. Possiamo quasi sempre dare al nostro server più CPU e memoria e modificarlo per renderlo più performante per i nostri utenti.

Il meglio di entrambi i mondi

Possiamo avere il meglio di entrambi i mondi quando si utilizza il rendering lato server con le moderne tecnologie frontend. Il modo in cui funziona generalmente è che il server esegue il rendering e restituisce l’applicazione completamente renderizzata al primo carico. Il passo successivo, noto come idratazione, è dove il tuo bundle JavaScript verrà scaricato ed eseguito. Questo collega i gestori di eventi e collega le cose come il tuo router lato client.

Con questo approccio, si ottengono tutti i vantaggi di SSR sul carico iniziale, quindi ogni interazione da quel punto in avanti verrà gestita da JavaScript lato client. Ciò prevede un carico iniziale veloce e SEO-friendly, seguito dall’esperienza dinamica dell’app Web a pagina singola che conosciamo e amiamo.

Applicazioni come questa sono note come applicazioni universali perché lo stesso JavaScript viene eseguito sul client e sul server. Potresti anche sentire il termine più elaborato “isomorfo” usato, il che significa esattamente la stessa cosa.

Tutorial: Implementare SSR

SSR non è privo di compromessi. Aggiunge un sovraccarico al tuo sviluppo introducendo una configurazione più complessa oltre a dover ospitare e gestire il tuo server. Questi problemi sono il motivo per cui quadri incredibili come Next.js e Razzle sono molto popolari: astraggono la parte di configurazione SSR e ti permettono di concentrarti sulla scrittura del codice dell’interfaccia utente.

In questo tutorial, non useremo alcun framework SSR. Il modo migliore per imparare come funziona qualcosa è effettivamente costruirlo, quindi impareremo come creare la configurazione SSR più semplice che possiamo fornire:

  • CDN globale
  • API backend completamente funzionale
  • Nessun server o infrastruttura da gestire
  • Distribuzione a comando singolo

Distribuiremo un’applicazione React renderizzata da server universale creata con create-react-app su Amazon Web Services (AWS). Non è necessario avere esperienza con AWS per seguire.

I nostri strumenti

Al fine di costruire la nostra applicazione, ci accingiamo a fare uso di alcuni diversi servizi AWS.

  • AWS Amplify: Un framework di alto livello per la gestione dei servizi AWS, principalmente per lo sviluppo mobile e web
  • AWS Lambda: Esegui codice nel cloud senza gestire server
  • AWS Cloudfront (CDN): una rete di distribuzione dei contenuti responsabile della distribuzione e della memorizzazione nella cache dei contenuti in tutto il mondo
  • AWS Simple Storage Service (S3): dove memorizzeremo le nostre risorse statiche)

Diagramma dell’architettura

La nostra funzione Lambda è responsabile del rendering del server della nostra applicazione React. Useremo S3 per archiviare i nostri contenuti statici e Cloudfront CDN per servirli. Non è necessario avere alcuna conoscenza preliminare di questi servizi, poiché AWS Amplify renderà estremamente semplice per noi crearli.

 Il nostro diagramma di architettura

Il nostro diagramma di architettura

Creazione della nostra applicazione

Prima di tutto, devi installare AWS Amplify CLI e creare un account AWS se non ne hai già uno. È possibile farlo seguendo questa breve guida.

Configurazione del progetto

Ora che Amplify è configurato, possiamo iniziare a configurare il nostro progetto React. Useremo la fantastica app create-react per aiutarci. Supponendo che tu abbia il Nodo.js e npm installati, possiamo eseguire:

npx create-react-app amplify-ssrcd amplify-ssr yarn add aws-amplify amplify init

Selezionare le opzioni predefinite nella procedura guidata AWS Amplify.

Il nostro progetto React è ora bootstrap con Amplify e pronto per noi per aggiungere il nostro “server” per SSR. Lo facciamo eseguendo amplify add api e rispondendo ad alcune domande:

$ amplify add api? Please select from one of the below mentioned services: REST? Provide a friendly name for your resource to be used as a label for this category in the project: amplifyssr? Provide a path (e.g., /items): /ssr? Choose a Lambda source: Create a new Lambda function? Provide a friendly name for your resource to be used as a label for this category in the project: amplifyssr? Provide the AWS Lambda function name: ssr? Choose the function runtime that you want to use: NodeJS? Choose the function template that you want to use: Serverless expressJS function? Do you want to access other resources created in this project from your Lambda function? N? Do you want to edit the local lambda function now? N? Restrict API access: N? Do you want to add another path? N

Ciò creerà i modelli, le directory e il codice pertinenti necessari per la nostra infrastruttura AWS e il backend: una funzione AWS Lambda che eseguirà un piccolo server Express responsabile del rendering della nostra applicazione React.

Prima di implementare la nostra infrastruttura, ci sono alcune modifiche che dobbiamo apportare all’interno della nostra applicazione React per prepararla per il rendering lato server. Apri src/App.js (il componente principale dell’app per l’applicazione React) e incolla quanto segue:

import React from 'react';function App() { return ( <div className="App"> <header className="App-header"> Server Rendered React App </header> </div> );}export default App;

Successivamente, dobbiamo creare uno script che renderà la nostra applicazione React sul lato server. Questo viene fatto con la funzione renderToString nel pacchetto react-dom/server. Questa funzione è responsabile di prendere il nostro componente <App /> e renderlo sul lato server come una stringa, pronta per essere restituita come HTML completamente renderizzato al client.

Crea un file in src/render.js con il seguente codice:

import React from "react";import { renderToString } from "react-dom/server";import App from "./App";export default () => renderToString(<App />);

Grande-il nostro client-side React app ha tutto il codice di cui ha bisogno per essere reso sul lato server. Ciò significa che ora dobbiamo codificare l’endpoint lato server che renderà la nostra applicazione React.

Abbiamo un problema, però — abbiamo bisogno della funzione src/render e del nostro codice componente <App /> da eseguire sul lato server. Il server non sa nulla di React o anche di moduli ES per impostazione predefinita. Per questo motivo, traspileremo il codice dall’applicazione React usando Babel nel lato server.

Per fare ciò, installiamo alcune dipendenze Babel nel nostro progetto.

yarn add --dev @babel/core @babel/cli @babel/preset-react @babel/preset-env

Quindi, crea un .babelrc alla radice del tuo progetto. Questo file è usato per configurare Babel e dirgli quali plugin / preset usare.

{ "presets":}

Infine, aggiorniamo il nostro package.json per traspilare il nostro codice come parte della fase di compilazione. Questo traspilerà i file nella directory amplify/backend/function/amplifyssr/src/client, che è dove memorizzeremo tutto il JavaScript universale che deve essere eseguito sul lato client e sul server per SSR.

 "scripts": { "start": "react-scripts start", "transpile": "babel src --out-dir amplify/backend/function/amplifyssr/src/client --copy-files", "build": "npm run transpile && react-scripts build && npm run copy", "copy": "cp build/index.html amplify/backend/function/amplifyssr/src/client", "test": "react-scripts test", "eject": "react-scripts eject" },

Rendering dell’app in Lambda

La configurazione di compilazione è completa! Saltiamo in amplify/backend/function/amplifyssr/src e installiamo react e react-dom, poiché saranno entrambi necessari per il Lambda per eseguire SSR.

yarn add react react-dom

Ora per configurare il nostro server Express, che verrà eseguito su Lambda. La funzione Lambda è stata generata automaticamente per noi quando abbiamo completato il passaggio amplify add api in precedenza e abbiamo scelto un’API REST e ExpressJS.

Amplify ha già configurato il server Express per l’esecuzione su Lambda, quindi tutto ciò che dobbiamo fare ora è aggiungere un endpoint al server-rendere la nostra applicazione React quando qualcuno colpisce l’URL dell’API nel browser. Aggiorna il tuo file amplify/backend/function/amplifyssr/src/app.js per contenere il seguente codice:

/* Amplify Params - DO NOT EDIT ENV REGIONAmplify Params - DO NOT EDIT */const express = require('express')const bodyParser = require('body-parser')const awsServerlessExpressMiddleware = require('aws-serverless-express/middleware')const fs = require('fs');const render = require('./client/render').default;// declare a new express appconst app = express()app.use(bodyParser.json())app.use(awsServerlessExpressMiddleware.eventContext())// Enable CORS for all methodsapp.use(function(req, res, next) { res.header("Access-Control-Allow-Origin", "*") res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept") next()});app.get('*', function(req, res) { // Read the index.html file from the create-react-app build const html = fs.readFileSync("./client/index.html", "utf-8"); // Server side render the react application const markup = render(); // Replace the empty body of index.html with the fully server rendered react application and send it back to the client res.send(html.replace(`<div></div>`, `<div>${markup}</div>`))});module.exports = app

Il nostro server Express è ora SSR-ready, e possiamo distribuire la nostra applicazione React.

Hosting e tocchi finali

Una volta ricevuto l’HTML reso dal server dal rendering iniziale della nostra app, recupereremo il pacchetto JavaScript lato client per subentrare da lì e fornirci una SPA completamente interattiva.

Abbiamo bisogno di un posto dove ospitare i nostri file JavaScript e statici lato client. In AWS, il servizio generalmente utilizzato per questo è S3 (Simple Storage Service), un archivio di oggetti cloud massicciamente scalabile.

Metteremo anche una CDN di fronte ad essa per il caching globale e le prestazioni. Con Amplify, possiamo creare entrambe queste risorse per il nostro progetto eseguendo alcuni comandi dalla nostra directory principale del progetto:

$ amplify add hostingSelect the plugin module to execute Amazon CloudFront and S3? Select the environment setup: PROD (S3 with CloudFront using HTTPS)? hosting bucket name (name your bucket or use the default)

Ora è possibile distribuire l’intera infrastruttura, inclusa la funzione Lambda Express Server, il bucket S3 e la CDN, eseguendo il comando amplify publish.

L’output della console mostrerà tutte le risorse rilevanti dei modelli creati per te da Amplify. Tieni presente che la creazione di una CDN Cloudfront può richiedere un po ‘ di tempo, quindi sii paziente. Una volta create le risorse, l’URL CDN di Cloudfront verrà visualizzato nel terminale.

Publish started for S3AndCloudFront✔ Uploaded files successfully.Your app is published successfully.https://d3gdcgc9a6lz30.cloudfront.net

L’ultima cosa che dobbiamo fare è dire a React da dove recuperare il nostro bundle lato client dopo che l’app è stata resa dal server. Questo viene fatto in create-react-app utilizzando la variabile di ambiente PUBLIC_URL. Aggiorniamo di nuovo la nostra app React package.json script per assomigliare al seguente:

 "scripts": { "start": "react-scripts start", "transpile": "babel src --out-dir amplify/backend/function/amplifyssr/src/client --copy-files", "build": "npm run transpile && PUBLIC_URL=<your-cloudfront-url> react-scripts build && npm run copy", "copy": "cp build/index.html amplify/backend/function/amplifyssr/src/client", "test": "react-scripts test", "eject": "react-scripts eject" },

Ricostruisci e distribuisci la tua applicazione in AWS con questa configurazione aggiornata.

amplify publish

Ora dovremmo avere un’app React completamente renderizzata lato server in esecuzione su AWS!

Esecuzione della nostra app

L’URL dell’API SSR può essere trovato su amplify/backend/amplify-meta.json. Cerca RootUrl nel tuo file JSON e dovresti vedere l’URL in cui puoi visitare la tua nuova applicazione renderizzata dal server. Dovrebbe essere simile al seguente:

"output": { "ApiName": "amplifyssr", "RootUrl": "https://g6nfj3bvsg.execute-api.eu-west-1.amazonaws.com/dev", "ApiId": "g6nfj3bvsg"}, 

Visita l’URL del gateway API nel tuo browser a <your-api-url>/ssr e dovresti vedere la tua nuova applicazione React con rendering del server! Se ti immergi nella scheda Rete nel tuo browser preferito e visualizzi le richieste, noterai che la richiesta a /ssr ha una risposta HTML completamente renderizzata con la nostra applicazione React renderizzata all’interno di <body> del documento.

<div> <div class="App" data-reactroot=""> <header class="App-header">Server Rendered React App</header> </div></div>

Noterai anche le richieste fatte al tuo URL Cloudfront dal browser per caricare il JavaScript lato client che prenderà il controllo del rendering da qui, dandoci il meglio sia del mondo di rendering lato client che lato server.

Dove andare da qui

Questo tutorial ha lo scopo di ottenere installato e funzionante con il rendering lato server il più rapidamente possibile, senza preoccuparsi di gestire l’infrastruttura, CDN, e altro ancora. Avendo usato l’approccio serverless, ci sono alcuni miglioramenti che possiamo apportare alla nostra configurazione.

Concorrenza con provisioning

Un modo in cui AWS Lambda è in grado di rimanere estremamente a basso costo è che le funzioni Lambda che non sono state colpite da un po ‘ andranno “inattive.”Questo significa essenzialmente che quando li eseguiamo di nuovo, ci sarà quello che è noto come” partenza a freddo ” — un ritardo di inizializzazione che deve accadere prima che il Lambda risponda.

Dopo questo, il lambda viene nuovamente “caldo” per un periodo di tempo e risponderà rapidamente alle richieste successive fino al successivo lungo periodo di inattività. Ciò può causare tempi di risposta leggermente inaffidabili.

Nonostante sia “serverless”, Lambda utilizza contenitori leggeri per elaborare qualsiasi richiesta. Ogni contenitore può elaborare solo una richiesta in un dato momento. Oltre al problema di avvio a freddo dopo un periodo di inattività, lo stesso vale anche quando molte richieste simultanee colpiscono la stessa funzione Lambda, causando l’avvio a freddo di più contenitori o lavoratori simultanei prima di rispondere.

In passato, molti ingegneri hanno risolto questo problema scrivendo script per eseguire periodicamente il ping del Lambda per tenerlo caldo. Ora c’è un modo nativo AWS molto migliore per risolvere questo problema, ed è noto come Concorrenza Provisioned.

Con la concorrenza fornita, è possibile richiedere molto facilmente un determinato numero di contenitori dedicati per rimanere caldi per una specifica funzione Lambda. Questo ti darà un tempo di risposta SSR molto più coerente in tempi di carico elevato e sporadico.

Versioni Lambda

È possibile creare diverse versioni Lambda per le proprie funzioni e dividere il traffico tra di esse. Questo è molto potente nella nostra applicazione SSR in quanto ci permette di effettuare aggiornamenti sul lato Lambda e A/B testarli con una porzione più piccola di utenti.

È possibile pubblicare diverse versioni del Lambda e dividere il traffico tra di loro in pesi specificati. Ad esempio, è possibile eseguire il rendering su server di un banner CTA per alcuni utenti per misurare l’impegno, ma non per altri. Puoi farlo con le versioni Lambda.

Applicazione Web full-stack

Come spiegato in precedenza, AWS Amplify crea già un’API REST e un server Express per noi, in cui abbiamo creato un endpoint per il rendering del server della nostra applicazione React. Possiamo sempre aggiungere più codice ed endpoint a questo server Express a amplify/backend/function/amplifyssr/src/app.js, permettendoci di trasformare la nostra app in un’applicazione Web full-stack, completa di database, autenticazione e altro ancora.

Puoi utilizzare la fantastica suite di strumenti AWS Amplify per creare queste risorse o collegarle alla tua infrastruttura, anche se non è ospitata su AWS. Puoi trattare il tuo backend AWS Lambda come qualsiasi altro server Express e costruire su di esso.

Hai già impostato l’intera pipeline di distribuzione eseguendo amplify publish in modo da poterti concentrare sulla scrittura del codice. Il punto di partenza in questo tutorial ti dà la massima flessibilità per fare quello che vuoi da qui.

Conclusione

Il rendering lato server non deve essere difficile. Possiamo usare strumenti completamente gestiti come Next o Razzle, che sono incredibili a pieno titolo, ma per molti team questo può essere un cambio di paradigma troppo grande dato il loro codice o requisiti esistenti. L’utilizzo di un approccio personalizzato semplice e a bassa manutenzione può semplificarti la vita, soprattutto se stai già utilizzando AWS o Amplify per il tuo progetto.

SSR può aggiungere un sacco di valore per le applicazioni web e fornire una performance tanto necessaria o spinta SEO. Siamo fortunati nella comunità di sviluppo web di avere strumenti in grado di creare CDN, backend serverless e applicazioni web completamente ospitate con pochi comandi o clic.

Anche se non pensi di aver bisogno di SSR, è un argomento molto diffuso e comune nell’ecosistema JavaScript. Avere una comprensione dei suoi benefici e compromessi sarà utile a quasi tutti coloro che sono coinvolti nella sfera dello sviluppo web.

Spero che tu abbia imparato qualcosa oggi-grazie per la lettura! Sentiti libero di contattarmi o seguirmi su Twitter, dove tweet e blog su JavaScript, Python, AWS, automazione e sviluppo senza codice.

La piena visibilità sulle app Production React

Il debug delle applicazioni React può essere difficile, soprattutto quando gli utenti riscontrano problemi difficili da riprodurre. Se sei interessato al monitoraggio e al monitoraggio dello stato di Redux, alla visualizzazione automatica degli errori JavaScript e al monitoraggio delle richieste di rete lente e del tempo di caricamento dei componenti, prova LogRocket.  LogRocket Dashboard Prova gratuita Banner

LogRocket è come un DVR per le applicazioni web, la registrazione letteralmente tutto ciò che accade sul vostro Reagire app. Invece di indovinare perché si verificano problemi, è possibile aggregare e riferire in quale stato si trovava l’applicazione quando si è verificato un problema. LogRocket monitora anche le prestazioni della tua app, riportando metriche come il carico della CPU del client, l’utilizzo della memoria del client e altro ancora.

Il pacchetto middleware LogRocket Redux aggiunge un ulteriore livello di visibilità alle sessioni utente. LogRocket registra tutte le azioni e lo stato dai tuoi negozi Redux.

Modernizza il modo in cui esegui il debug delle tue app React-avvia il monitoraggio gratuitamente.

Lascia un commento

Il tuo indirizzo email non sarà pubblicato.