Cel mai bun din ambele lumi: SSR cu JavaScript izomorf

randare Server-side, sau SSR, este o frază pe care o auzi de multe ori în comunitatea de dezvoltare frontend.

la nivelul cel mai de bază, randarea pe partea de server este exact ceea ce descrie: redarea aplicațiilor pe server. Navigați la un site web, face o solicitare către server, redă unele HTML și obțineți rezultatul complet redat înapoi în browserul dvs. Destul de simplu. S-ar putea să vă întrebați De ce comunitatea are chiar și un cuvânt cheie pentru asta.

înainte de apariția aplicațiilor web bogate și dinamice care se bazau foarte mult pe JavaScript și jQuery, în esență toate aplicațiile web erau randate pe server. PHP, WordPress și chiar site-urile HTML de bază sunt exemple în acest sens.

când vizitați o pagină de pe unul dintre aceste site — uri, veți primi înapoi toate datele HTML și toate. Dacă faceți clic pe un link, browserul va face o altă solicitare către server. În urma răspunsului, browserul va reîmprospăta și va reda Pagina următoare de la bază. Această abordare funcționează bine și are de ani de zile; browserele sunt spectaculos de rapide la redarea HTML static. Ce s-a schimbat?

de la începutul secolului, utilizarea JavaScript a trecut de la o mică stropire aici și acolo pentru interactivitatea paginii web la limba de necontestat de alegere pe web. Livrăm în mod constant mai multă logică și JavaScript în browser.

cadre cu o singură pagină, cum ar fi React și Vue, au inaugurat această nouă eră a aplicațiilor web dinamice, complexe, bazate pe date. Aceste spa-uri diferă de aplicațiile prestate de server, deoarece nu aduc conținut complet redat complet cu date de pe server înainte de redare pe ecran.

aplicațiile randate din partea clientului își redau conținutul în browser folosind JavaScript. În loc să preia tot conținutul de pe server, pur și simplu aduc o pagină HTML barebones fără conținut corporal și redă tot conținutul din interior folosind JavaScript.

beneficiul acestui lucru este că evitați reîmprospătarea completă a paginii care se întâmplă cu aplicațiile complet prestate de server, ceea ce poate fi puțin deranjant pentru utilizator. Aplicațiile randate de client cu o singură pagină vor actualiza conținutul de pe ecran, vor prelua date din API-uri și se vor actualiza chiar în fața dvs. fără niciun fel de reîmprospătare a paginii. Această caracteristică este ceea ce face ca aplicațiile web moderne să se simtă rapide și „native” pe măsură ce interacționați cu ele.

client-side De redare compromisuri

nu e tot soare și curcubee în lumea SPA client-side-prestate. Există unele compromisuri care vin cu redare cererea dumneavoastră pe partea de client. Două exemple principale sunt SEO și performanța inițială de încărcare.

SEO

deoarece aplicațiile randate de client returnează o pagină HTML goală cu foarte puțin conținut înainte ca JavaScript să intre și să redea restul, poate fi dificil pentru crawlerele motoarelor de căutare să înțeleagă structura HTML a paginii dvs., ceea ce dăunează clasamentului de căutare al site-ului dvs. Google a făcut o mulțime de lucruri bune în acest sens, dar este totuși recomandat să evitați redarea din partea clientului dacă SEO este deosebit de important.

performanța inițială de încărcare

cu aplicațiile randate de client, vedeți în general următoarele lucruri când deschideți prima pagină:

  • aplicația încarcă unele HTML de bază, cum ar fi un shell app sau navbar static
  • veți vedea un indicator de încărcare de un fel
  • conținutul dvs. este apoi randat

problema cu acest lucru este că aplicația dvs. nu va arăta nimic până când JavaScript se încarcă complet din rețea și este terminat elemente de redare pe ecran.

pe scurt, problema cu performanța din partea clientului în general este că nu puteți controla pe ce dispozitiv client folosește cineva aplicația dvs.-fie că este vorba de smartphone — ul lor de ultimă generație, de o mașină desktop puternică de ultimă generație sau de un smartphone inferior de 100 USD.

cu toate acestea, controlăm serverul. Aproape întotdeauna putem oferi serverului nostru mai mult procesor și memorie și îl putem modifica pentru a-l face să funcționeze mai bine pentru utilizatorii noștri.

cel mai bun din ambele lumi

putem avea cel mai bun din ambele lumi atunci când se utilizează randare server-side cu tehnologii moderne frontend. Modul în care funcționează în general este că serverul redă și trimite înapoi aplicația complet redată la prima încărcare. Următorul pas, cunoscut sub numele de hidratare, este locul în care Pachetul JavaScript va fi descărcat și executat. Acest lucru atașează manipulatorii de evenimente și leagă lucrurile ca routerul dvs. din partea clientului.

cu această abordare, veți obține toate beneficiile SSR pe sarcina inițială, atunci fiecare interacțiune din acel moment înainte va fi tratată de client-side JavaScript. Acest lucru oferă o încărcare inițială rapidă, prietenoasă cu SEO, urmată de experiența dinamică a aplicației web cu o singură pagină pe care o cunoaștem și o iubim.

aplicații ca aceasta sunt cunoscute ca aplicații universale, deoarece același JavaScript rulează pe client și server. Puteți auzi, de asemenea, termenul crescator „izomorf” fiind folosit, ceea ce înseamnă exact același lucru.

Tutorial: implementarea SSR

SSR nu este fără compromisuri. Se adaugă deasupra capului la dezvoltarea dvs. prin introducerea unei configurații mai complexe, precum și necesitatea de a găzdui și gestiona propriul server. Aceste probleme sunt de ce cadre incredibile ca Next.js și Razzle sunt foarte populare: abstractizează partea de configurare SSR și vă permit să vă concentrați pe scrierea codului UI.

în acest tutorial, nu vom folosi niciun cadru SSR. Cel mai bun mod de a învăța cum funcționează ceva este prin construirea de fapt, așa că vom învăța cum să creeze cea mai simplă configurare SSR putem, eventual, care va oferi:

  • CDN Global
  • API backend complet funcțional
  • nu există servere sau infrastructură pentru a gestiona
  • implementare cu o singură comandă

vom implementa o aplicație react randată de server universal creată cu create-react-app pe Amazon Web Services (AWS). Nu trebuie să aveți experiență cu AWS pentru a urma.

instrumentele noastre

pentru a construi aplicația noastră, vom folosi câteva servicii AWS diferite.

  • AWS amplifica: Un cadru la nivel înalt pentru gestionarea serviciilor AWS, în principal pentru dezvoltarea mobilă și web
  • AWS Lambda: rulați codul în cloud fără a gestiona serverele
  • AWS Cloudfront (CDN): o rețea de livrare a conținutului responsabilă de livrarea și stocarea în cache a conținutului în întreaga lume
  • AWS Simple Storage Service (S3): unde vom stoca activele noastre statice (JS, CSS etc.)

diagrama arhitecturii

funcția noastră Lambda este responsabilă pentru redarea serverului aplicației noastre React. Vom folosi S3 pentru a stoca conținutul nostru static și CloudFront CDN pentru a-l servi. Nu trebuie să aveți cunoștințe prealabile despre aceste servicii, deoarece AWS Amplify va face foarte simplu pentru noi să le creăm.

diagrama noastră de arhitectură

diagrama noastră de arhitectură

construirea aplicației noastre

în primul rând, trebuie să instalați AWS Amplify CLI și să creați un cont AWS dacă nu aveți deja unul. Puteți face acest lucru urmând acest scurt ghid.

configurare proiect

acum că Amplify este configurat, putem începe configurarea proiectului nostru React. Vom folosi fantastic crea-react-app pentru a ne ajuta. Presupunând că aveți nod.JS și NPM instalat, putem rula:

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

selectați opțiunile implicite din Expertul AWS Amplify.

proiectul nostru React este acum bootstrapped cu Amplify și gata pentru a adăuga „serverul” nostru pentru SSR. Facem acest lucru rulând amplify add api și răspunzând la câteva întrebări:

$ 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

aceasta va crea șabloanele, directoarele și Codul relevante necesare pentru infrastructura și backend-ul nostru AWS: o funcție AWS Lambda care va rula un mic server expres responsabil pentru redarea aplicației noastre React.

înainte de a implementa infrastructura noastră, există unele modificări pe care trebuie să le facem în aplicația noastră React pentru a o pregăti pentru redarea pe partea de server. Deschideți src/App.js(componenta principală a aplicației pentru aplicația React) și inserați următoarele:

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

apoi, trebuie să creăm un script care să redea aplicația noastră React pe partea de server. Acest lucru se face cu funcția renderToString din pachetul react-dom/server. Această funcție este responsabilă pentru preluarea componentei <App /> și redarea acesteia pe partea serverului ca șir, gata de a fi returnat ca HTML complet redat clientului.

creați un fișier la src/render.js cu următorul cod:

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

excelent — aplicația noastră react din partea clientului are tot codul de care are nevoie pentru a fi redat pe partea serverului. Aceasta înseamnă că acum trebuie să codificăm punctul final din partea serverului care va face aplicația noastră React.

avem o problemă, deși — avem nevoie de src/render funcția și <App /> codul nostru componentă să fie rulat pe partea de server. Serverul nu știe nimic despre modulele React sau chiar ES în mod implicit. Din acest motiv, vom transpila codul din aplicația React folosind Babel în partea serverului.

pentru a face acest lucru, să instalăm câteva dependențe Babel în proiectul nostru.

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

apoi, creați un .babelrc la rădăcina proiectului. Acest fișier este folosit pentru a configura Babel și spune-i ce plugin-uri / presetări pentru a utiliza.

{ "presets":}

în cele din urmă, să actualizăm package.json pentru a transpila codul nostru ca parte a etapei de construire. Aceasta va transpila fișierele în directorul amplify/backend/function/amplifyssr/src/client, unde vom stoca toate JavaScript-urile universale care trebuie rulate atât pe partea clientului, cât și pe serverul pentru 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" },

Redarea aplicației în Lambda

configurația build este completă! Să sărim în amplify/backend/function/amplifyssr/src și să instalăm react și react-dom, deoarece ambele vor fi necesare pentru ca Lambda să efectueze SSR.

yarn add react react-dom

acum pentru a configura serverul nostru Express, care va rula pe Lambda. Funcția Lambda a fost generată automat pentru noi când am finalizat pasul amplify add api mai devreme și am ales un API REST și ExpressJS.

Amplify a configurat deja serverul Express pentru a rula pe Lambda, deci tot ce trebuie să facem acum este să adăugăm un punct final la server-redați aplicația noastră React atunci când cineva atinge adresa URL API din browser. Actualizați fișierul amplify/backend/function/amplifyssr/src/app.js pentru a conține următorul cod:

/* 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

serverul nostru Express este acum pregătit pentru SSR și putem implementa aplicația noastră React.

găzduire și atingeri finale

odată ce primim HTML-ul redat de server înapoi de la randarea inițială a aplicației noastre, vom prelua apoi pachetul JavaScript din partea clientului pentru a prelua de acolo și a ne oferi un SPA complet interactiv.

avem nevoie de un loc unde să găzduim fișierele noastre JavaScript și statice din partea clientului. În AWS, serviciul utilizat în general pentru acest lucru este S3 (Simple Storage Service), un magazin de obiecte cloud scalabil masiv.

de asemenea, vom pune un CDN în fața acestuia pentru cache și performanță globală. Cu Amplify, putem crea ambele resurse pentru proiectul nostru rulând câteva comenzi din directorul rădăcină al proiectului nostru:

$ 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)

acum puteți implementa întreaga infrastructură, inclusiv funcția Express Server Lambda, S3 bucket și CDN, executând comanda amplify publish.

ieșirea consolei dvs. va afișa toate resursele relevante din șabloanele create pentru dvs. de Amplify. Vă rugăm să rețineți că crearea unui CDN Cloudfront poate dura ceva timp, așa că aveți răbdare. Odată ce resursele dvs. sunt create, adresa URL CDN Cloudfront va fi afișată în terminal.

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

ultimul lucru pe care trebuie să-l facem este să-i spunem lui React de unde să ne aducă pachetul client-side după ce aplicația este redată de server. Acest lucru se face în create-react-app folosind PUBLIC_URL variabila de mediu. Să actualizăm aplicația noastră React package.json script-uri din nou să arate ca următoarele:

 "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" },

reconstruiți și implementați aplicația la AWS cu această configurație actualizată.

amplify publish

ar trebui să avem acum o aplicație React complet redată de server care rulează pe AWS!

rularea aplicației noastre

URL-ul SSR API pot fi găsite la amplify/backend/amplify-meta.json. Căutați RootUrl în fișierul JSON și ar trebui să vedeți adresa URL la care puteți vizita noua aplicație randată de server. Ar trebui să arate ceva de genul următor:

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

vizitați API Gateway URL-ul în browser-ul dvs. la <your-api-url>/ssr și ar trebui să vedeți noua aplicație strălucitoare Server-randat React! Dacă vă scufundați în fila Rețea din browserul ales și vizualizați solicitările, veți observa că solicitarea către /ssr are un răspuns HTML complet redat cu aplicația noastră React redată în <body> a documentului.

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

veți observa, de asemenea, solicitările făcute către adresa URL Cloudfront din browser pentru a încărca JavaScript-ul din partea clientului care va prelua redarea de aici, oferindu-ne cele mai bune atât din partea clientului, cât și din partea serverului.

unde să mergeți de aici

acest tutorial este destinat să vă pună în funcțiune cu redarea pe partea de server cât mai repede posibil, fără să vă faceți griji cu privire la gestionarea infrastructurii, CDN-urilor și multe altele. După ce am folosit abordarea fără server, există câteva îmbunătățiri frumoase pe care le putem face pentru configurarea noastră.

concurență Provizionată

o modalitate prin care AWS Lambda este capabilă să rămână extrem de ieftină este că funcțiile Lambda care nu au fost lovite de ceva timp vor merge „inactiv.”Acest lucru înseamnă în esență că atunci când le executăm din nou, va exista ceea ce este cunoscut sub numele de „pornire la rece” — o întârziere de inițializare care trebuie să se întâmple înainte ca Lambda să răspundă.

după aceasta, lambda este apoi „caldă” din nou pentru o perioadă de timp și va răspunde rapid la cererile ulterioare până la următoarea perioadă lungă de inactivitate. Acest lucru poate provoca timpi de răspuns ușor nesiguri.

în ciuda faptului că este „fără server”, Lambda folosește containere ușoare pentru a procesa orice solicitare. Fiecare container poate procesa o singură cerere la un moment dat. În plus față de problema de pornire la rece după o perioadă de inactivitate, același lucru este valabil și atunci când multe cereri concurente ating aceeași funcție Lambda, determinând pornirea la rece a mai multor containere sau lucrători concurenți înainte de a răspunde.

în trecut, o mulțime de ingineri au rezolvat această problemă scriind scripturi pentru a ping Lambda periodic pentru a-l menține cald. Există acum un mod mult mai bun AWS-nativ pentru a rezolva acest lucru, și este cunoscut ca Provisioned Concurrency.

cu concurență Provizionată, puteți solicita foarte ușor un anumit număr de containere dedicate pentru a rămâne cald pentru o anumită funcție Lambda. Acest lucru vă va oferi un timp de răspuns SSR mult mai consistent în perioade de încărcare ridicată și sporadică.

versiuni Lambda

puteți crea mai multe versiuni Lambda pentru funcțiile dvs. și puteți împărți traficul între ele. Acest lucru este foarte puternic în aplicația noastră SSR, deoarece ne permite să facem actualizări pe partea Lambda și A/B să le testăm cu o porțiune mai mică de utilizatori.

puteți publica mai multe versiuni ale Lambda și puteți împărți traficul între ele în greutăți pe care le specificați. De exemplu, poate doriți să faceți Server-render un banner CTA pentru unii utilizatori pentru a măsura implicarea, dar nu pentru alții. Puteți face acest lucru cu versiunile Lambda.

aplicație Web Full-stack

după cum sa explicat anterior, AWS Amplify creează deja un API REST și un server Express pentru noi, în care am creat un punct final pentru server-render aplicația noastră React. Putem adăuga întotdeauna mai multe coduri și puncte finale la acest server expres la amplify/backend/function/amplifyssr/src/app.js, permițându-ne să transformăm aplicația noastră într-o aplicație web cu stivă completă, completată cu baze de date, autentificare și multe altele.

puteți utiliza suita fantastică de instrumente AWS Amplify pentru a crea aceste resurse sau pentru a vă conecta la propria infrastructură — chiar dacă nu este găzduită pe AWS. Puteți trata backend-ul AWS Lambda ca orice alt server expres și puteți construi pe partea de sus a acestuia.

aveți deja întreaga conductă de implementare configurată rulând amplify publish, astfel încât să vă puteți concentra pe scrierea codului. Punctul de plecare din acest tutorial vă oferă flexibilitate totală pentru a face ceea ce doriți de aici.

concluzie

randare Server-side nu trebuie să fie greu. Putem folosi instrumente complet gestionate, cum ar fi Next sau Razzle, care sunt uimitoare în sine, dar pentru o mulțime de echipe, aceasta poate fi o schimbare de paradigmă mult prea mare, având în vedere codul sau cerințele lor existente. Folosind un simplu, low-întreținere, abordare personalizată poate face viața mai ușoară, mai ales dacă utilizați deja AWS sau Amplify pentru proiectul dumneavoastră.

SSR poate adăuga o tonă de valoare aplicațiilor dvs. web și poate oferi o performanță atât de necesară sau un impuls SEO. Suntem norocoși în comunitatea de dezvoltare web să avem instrumente care pot crea CDN-uri, backend-uri fără server și aplicații web găzduite complet cu câteva comenzi sau clicuri.

chiar dacă nu credeți că aveți nevoie de SSR, este un subiect foarte răspândit și comun în ecosistemul JavaScript. Înțelegerea beneficiilor și compromisurilor sale va fi utilă pentru aproape oricine este implicat în sfera dezvoltării web.

sper că ați învățat ceva astăzi — mulțumesc pentru lectură! Simțiți-vă liber să mă contactați sau să mă urmați pe Twitter, unde am tweet și blog despre JavaScript, Python, AWS, automatizare și dezvoltare fără cod.

vizibilitate completă în producție React apps

depanarea aplicațiilor React poate fi dificilă, mai ales atunci când utilizatorii întâmpină probleme dificil de reprodus. Dacă sunteți interesat de monitorizarea și urmărirea stării Redux, de apariția automată a erorilor JavaScript și de urmărirea cererilor lente de rețea și a timpului de încărcare a componentelor, încercați LogRocket. Logrocket Dashboard Free Trial Banner

LogRocket este ca un DVR pentru aplicații web, înregistrând literalmente tot ce se întâmplă în aplicația React. În loc să ghiciți de ce se întâmplă probleme, puteți agrega și raporta în ce stare se afla aplicația dvs. atunci când a apărut o problemă. LogRocket monitorizează, de asemenea, performanța aplicației dvs., raportând cu valori precum încărcarea procesorului clientului, utilizarea memoriei clientului și multe altele.

Pachetul logrocket Redux middleware adaugă un strat suplimentar de vizibilitate în sesiunile de utilizator. LogRocket înregistrează toate acțiunile și starea din magazinele dvs.

modernizați modul în care depanați aplicațiile React — începeți monitorizarea gratuit.

Lasă un răspuns

Adresa ta de email nu va fi publicată.