Isomorf applikation med React og Nashorn

1. Oversigt

I denne vejledning forstår vi, hvad der præcist er en isomorf app. Vi diskuterer også Nashorn, JavaScript-motoren, der følger med Java.

Desuden undersøger vi, hvordan vi kan bruge Nashorn sammen med et frontend-bibliotek som React til at oprette en isomorf app.

2. En lille smule historie

Traditionelt blev klient- og serverapplikationer skrevet på en måde, der var ret tung på serversiden. Tænk på PHP som en scriptmotor, der genererer mest statisk HTML og webbrowsere, der gengiver dem.

Netscape fulgte med understøttelse af JavaScript i sin browser tilbage i midten af ​​halvfemserne. Det begyndte at skifte noget af behandlingen fra serversiden til klientsiden. I lang tid kæmpede udviklere med forskellige problemer vedrørende JavaScript-understøttelse i webbrowsere.

Med den voksende efterspørgsel efter hurtigere og interaktiv brugeroplevelse blev grænsen allerede skubbet hårdere. En af de tidligste rammer, der ændrede spillet, var jQuery. Det bragte flere brugervenlige funktioner og meget forbedret support til AJAX.

Snart begyndte mange rammer for front-end-udvikling at dukke op, hvilket forbedrede udviklerens oplevelse meget. Fra og med AngularJS fra Google, React fra Facebook og senere Vue, begyndte de at fange udviklerens opmærksomhed.

Med moderne browsersupport, bemærkelsesværdige rammer og nødvendige værktøjer, tidevandet skifter stort set mod klientsiden.

En fordybende oplevelse på stadig hurtigere håndholdte enheder kræver mere behandling på klientsiden.

3. Hvad er en isomorf app?

Så vi så, hvordan frontend-rammer hjælper os med at udvikle en webapplikation, hvor brugergrænsefladen gengives fuldstændigt på klientsiden.

Imidlertid, det er også muligt at bruge den samme ramme på serversiden og generere den samme brugergrænseflade.

Nu behøver vi ikke nødvendigvis at holde os til kun klientsiden eller kun server-side-løsninger. En bedre måde er at have en løsning, hvor klienten og serveren kan begge behandle den samme front-end-kode og generere den samme brugergrænseflade.

Der er fordele ved denne tilgang, som vi vil diskutere senere.

Sådanne webapplikationer kaldes isomorfe eller universelle. Nu er sprog på klientsiden mest udelukkende JavaScript. Derfor, for at en isomorf app kan fungere, skal vi også bruge JavaScript på serversiden.

Node.js er langt det mest almindelige valg at opbygge en server-renderet applikation.

4. Hvad er Nashorn?

Så hvor passer Nashorn ind, og hvorfor skal vi bruge det? Nashorn er en JavaScript-motor pakket som standard med Java. Derfor, hvis vi allerede har en webapplikation i Java og vil bygge en isomorf app, er Nashorn ret praktisk!

Nashorn er blevet frigivet som en del af Java 8. Dette er primært fokuseret på at tillade indlejrede JavaScript-applikationer i Java.

Nashorn kompilerer JavaScript i hukommelsen til Java Bytecode og sender det til JVM til eksekvering. Dette giver bedre ydelse sammenlignet med den tidligere motor, Rhino.

5. Oprettelse af en isomorf app

Vi har gennemgået nok kontekst nu. Vores applikation her viser en Fibonacci-sekvens og giver en knap til at generere og vise det næste nummer i sekvensen. Lad os oprette en simpel isomorf app nu med en back-end og front-end:

  • Front-end: En simpel React.js-baseret front-end
  • Back-end: En simpel Spring Boot-back-end med Nashorn til behandling af JavaScript

6. Application Front-End

Vi bruger React.js til oprettelse af vores frontend. React er et populært JavaScript-bibliotek til opbygning af apps med en side. Det hjælper os med at nedbryde en kompleks brugergrænseflade i hierarkiske komponenter med valgfri tilstandsbinding og envejsbinding.

React analyserer dette hierarki og opretter en datastruktur i hukommelsen kaldet virtuel DOM. Dette hjælper React med at finde ændringer mellem forskellige tilstande og foretage minimale ændringer i browserens DOM.

6.1. Reager komponent

Lad os oprette vores første React-komponent:

var App = React.createClass ({displayName: "App", handleSubmit: function () {var last = this.state.data [this.state.data.length-1]; var secondLast = this.state.data [denne .state.data.length-2]; $ .ajax ({url: '/ next /' + last + '/' + secondLast, dataType: 'text', success: function (msg) {var series = this.state. data; series.push (msg); this.setState ({data: series});} .bind (this), error: function (xhr, status, err) {console.error ('/ next', status, err .toString ());} .bind (dette)});}, componentDidMount: funktion () {this.setState ({data: this.props.data});}, getInitialState: funktion () {return {data: []};}, gengiv: funktion () {return (React.createElement ("div", {className: "app"}, React.createElement ("h2", null, "Fibonacci Generator"), React.createElement ( "h2", null, this.state.data.toString ()), React.createElement ("input", {type: "send", værdi: "Næste", onClick: this.handleSubmit})))}}} );

Lad os nu forstå, hvad den ovenstående kode gør:

  • Til at begynde med har vi defineret en klassekomponent i React kaldet "App"
  • For det meste vigtig funktion inde i denne komponent er “render”, som er ansvarlig for at generere brugergrænsefladen
  • Vi har leveret en stil className som komponenten kan bruge
  • Vi bruger komponenttilstanden her til at gemme og vise serien
  • Mens tilstanden initialiseres som en tom liste, henter den data, der er sendt til komponenten som en prop, når komponenten monteres
  • Endelig foretages et jQuery-opkald til REST-tjenesten ved at klikke på knappen "Tilføj"
  • Opkaldet henter det næste nummer i sekvensen og føjer det til komponentens tilstand
  • Ændring i komponentens tilstand gengiver automatisk komponenten igen

6.2. Brug af reaktionskomponenten

React ser efter et navngivet "div" -element på HTML-siden for at forankre dets indhold. Alt, hvad vi skal gøre, er at give en HTML-side med dette "div" -element og indlæse JS-filerne:

   Hej React ReactDOM.render (React.createElement (App, {data: [0,1,1]}), document.getElementById ("root")); 

Så lad os se, hvad vi har gjort her:

  • Vi importerede de krævede JS-biblioteker, reagerer, reagerer-dom og jQuery
  • Derefter definerede vi et "div" -element kaldet "root"
  • Vi importerede også JS-filen med vores React-komponent
  • Dernæst kaldte vi React-komponenten "App" med nogle frødata, de første tre Fibonacci-numre

7. Application Back-End

Lad os nu se, hvordan vi kan skabe en passende back-end til vores applikation. Det har vi allerede besluttet Brug Spring Boot sammen med Spring Web til opbygning af denne applikation. Endnu vigtigere har vi besluttet at brug Nashorn til at behandle JavaScript-baseret front-end vi udviklede i sidste afsnit.

7.1. Maven afhængigheder

Til vores enkle anvendelse bruger vi JSP sammen med Spring MVC, så vi tilføjer et par afhængigheder til vores POM:

 org.springframework.boot spring-boot-starter-web org.apache.tomcat.embed tomcat-embed-jasper forudsat 

Den første er standardfjederstøveafhængighed for en webapplikation. Den anden er nødvendig for at kompilere JSP'er.

7.2. Webcontroller

Lad os nu oprette vores webcontroller, som behandler vores JavaScript-fil og returnerer en HTML ved hjælp af JSP:

@Controller public class MyWebController {@RequestMapping ("/") public String index (Map model) throw Exception {ScriptEngine nashorn = new ScriptEngineManager (). GetEngineByName ("nashorn"); nashorn.eval (ny FileReader ("statisk / js / react.js")); nashorn.eval (ny FileReader ("static / js / react-dom-server.js")); nashorn.eval (ny FileReader ("statisk / app.js")); Objekt html = nashorn.eval ("ReactDOMServer.renderToString (" + "React.createElement (App, {data: [0,1,1]})" + ");"); model.put ("indhold", String.valueOf (html)); returner "indeks"; }}

Så hvad sker der præcist her:

  • Vi henter en forekomst af ScriptEngine af typen Nashorn fra ScriptEngineManager
  • Så vi indlæse relevante biblioteker til React, react.js og react-dom-server.js
  • Vi indlæser også vores JS-fil, der har vores reaktionskomponent "App"
  • Endelig evaluerer vi et JS-fragment, der skaber reaktionselement med komponenten "App" og nogle frødata
  • Dette giver os en output af React, et HTML-fragment som Objekt
  • Vi sender dette HTML-fragment som data til den relevante visning - JSP

7.3. JSP

Nu, hvordan behandler vi dette HTML-fragment i vores JSP?

Husk, at React automatisk tilføjer sin output til et navngivet "div" -element - "root" i vores tilfælde. Imidlertid, vi tilføjer vores server-genererede HTML-fragment til det samme element manuelt i vores JSP.

Lad os se, hvordan JSP ser ud nu:

   Hej Reager! $ {content} ReactDOM.render (React.createElement (App, {data: [0,1,1]}), document.getElementById ("root")); 

Dette er den samme side, som vi oprettede tidligere, bortset fra det faktum, at vi har tilføjet vores HTML-fragment i "root" div, som var tom tidligere.

7.4. REST-controller

Endelig har vi også brug for et REST-slutpunkt på serversiden, der giver os det næste Fibonacci-nummer i sekvensen:

@RestController public class MyRestController {@RequestMapping ("/ next / {last} / {secondLast}") public int index (@PathVariable ("last") int last, @PathVariable ("secondLast") int secondLast) kaster Undtagelse {return sidste + sekund Sidste; }}

Intet fancy her, bare en simpel Spring REST-controller.

8. Kørsel af applikationen

Nu hvor vi har afsluttet vores front-end såvel som vores back-end, er det tid til at køre applikationen.

Vi bør starte Spring Boot-applikationen normalt ved at bruge bootstrapping-klassen:

@SpringBootApplication public class Application udvider SpringBootServletInitializer {@ Override-beskyttet SpringApplicationBuilder-konfiguration (SpringApplicationBuilder-applikation) {return application.sources (Application.class); } offentlig statisk ugyldig hoved (String [] args) kaster Undtagelse {SpringApplication.run (Application.class, args); }}

Når vi kører denne klasse, Spring Boot kompilerer vores JSP'er og gør dem tilgængelige på indlejrede Tomcat sammen med resten af ​​webapplikationen.

Hvis vi besøger vores side, ser vi nu:

Lad os forstå rækkefølgen af ​​begivenheder:

  • Browseren anmoder om denne side
  • Når anmodningen om denne side ankommer, behandler Spring webcontroller JS-filerne
  • Nashorn-motor genererer et HTML-fragment og sender dette til JSP
  • JSP tilføjer dette HTML-fragment til "root" div-elementet og returnerer endelig ovenstående HTML-side
  • Browseren gengiver HTML og begynder i mellemtiden at downloade JS-filer
  • Endelig er siden klar til handlinger på klientsiden - vi kan tilføje flere numre i serien

Den vigtige ting at forstå her er, hvad der sker, hvis React finder et HTML-fragment i mål-"div" -elementet. I sådanne tilfælde, React sammenligner dette fragment med hvad det har og erstatter det ikke, hvis det finder et læseligt fragment. Dette er nøjagtigt, hvad der giver server-rendering og isomorfe apps.

9. Hvad er der mere muligt?

I vores enkle eksempel har vi lige ridset overfladen af, hvad der er muligt. Front-end applikationer med moderne JS-baserede rammer bliver stadig mere kraftfulde og komplekse. Med denne ekstra kompleksitet er der mange ting, vi skal tage os af:

  • Vi har oprettet kun en React-komponent i vores applikation, når det i virkeligheden kan være flere komponenter danner et hierarki som sender data gennem rekvisitter
  • Vi vil gerne Opret separate JS-filer til hver komponent for at holde dem håndterbare og styre deres afhængigheder gennem "eksport / kræve" eller "eksport / import"
  • Desuden er det måske ikke kun muligt at styre tilstand inden for komponenter; vi måske ønsker at bruge et statsforvaltningsbibliotek som Redux
  • Desuden er vi muligvis nødt til at interagere med eksterne tjenester som bivirkninger af handlinger; dette kan kræve, at vi bruger et mønster som redux-thunk eller Redux-Saga
  • Vigtigst af alt vil vi gerne gearing JSX, en syntaksudvidelse til JS til beskrivelse af brugergrænsefladen

Mens Nashorn er fuldt kompatibel med ren JS, understøtter den muligvis ikke alle de ovennævnte funktioner. Mange af disse kræver trans-kompilering og polyfills på grund af JS-kompatibilitet.

Den sædvanlige praksis i sådanne tilfælde er at udnytte en modulbundler som Webpack eller Rollup. Hvad de primært gør, er at behandle alle React-kildefiler og samle dem i en enkelt JS-fil sammen med alle afhængigheder. Dette kræver altid en moderne JavaScript-kompilator som Babel for at kompilere JavaScript for at være bagudkompatibel.

Den endelige pakke har kun gode gamle JS, som browsere kan forstå, og Nashorn overholder også.

10. Fordele ved en isomorf app

Så vi har talt meget om isomorfe apps og har endda oprettet en simpel applikation nu. Men hvorfor skulle vi ligefrem bekymre os om dette? Lad os forstå nogle af de vigtigste fordele ved at bruge en isomorf app.

10.1. Første side gengivelse

En af de mest betydningsfulde fordele ved en isomorf app er hurtigere gengivelse af første side. I den typiske gengivne applikation på klientsiden begynder browseren med at downloade alle JS- og CSS-artefakter.

Derefter indlæser de og begynder at gengive den første side. Hvis vi sender den første side, der er gengivet fra serversiden, kan dette være meget hurtigere og give en forbedret brugeroplevelse.

10.2. SEO-venlig

En anden fordel ofte citeret med rendering på serversiden er relateret til SEO. Det antages, at søgebots ikke er i stand til at behandle JavaScript og derfor ikke kan se en indeksside, der gengives på klientsiden gennem biblioteker som React. En gengivet side på serversiden er derfor SEO-venligere. Det er dog værd at bemærke, at moderne søgemaskine-bots hævder at behandle JavaScript.

11. Konklusion

I denne vejledning gennemgik vi de grundlæggende koncepter for isomorfe applikationer og Nashorn JavaScript-motoren. Vi udforskede yderligere, hvordan man bygger en isomorf app med Spring Boot, React og Nashorn.

Derefter diskuterede vi de andre muligheder for at udvide front-end-applikationen og fordelene ved at bruge en isomorf app.

Som altid kan koden findes på GitHub.


$config[zx-auto] not found$config[zx-overlay] not found