Java >> Java opplæring >  >> JVM

Bygg isomorfe webapper på JVM med React.js og Spring Boot

Denne artikkelen viser hvordan du kombinerer React.js med Java og Spring Boot for å forhåndsrendere fullverdige MVC-frontends på JVM-serveren uten å tenke på Node.js.

Vente. Isomorfe nettapper? Hva pokker er det?

Isomorfe JavaScript-apper er JavaScript-applikasjoner som kan kjøre både klient- og serverside. Backend og frontend deler samme kode.

Tradisjonelt genererer webapper HTML på serveren og sender den over til klienten. Dette har endret seg med den nylige økningen av MVC-rammeverk på klientsiden som Angular.js, Backbone.js eller Ember. Men å generere HTML-visninger på klienten har både fordeler og ulemper. Isomorfe webapper prøver å lukke dette gapet ved å la deg bruke de samme teknologiene for å generere visninger både på serveren og på klienten.

React.js er et ferskt nytt JavaScript-bibliotek for å generere visninger programmatisk. React er ikke et komplett MVC-rammeverk - det er V i MVC, som konsentrerer seg om å lage og administrere visninger ved å dele opp hele brukergrensesnittet i komponenter. Disse React-komponentene kan gjengis både på klienten og på serveren.

Nashorn JavaScript-motoren gjør isomorfe webapper på JVM mulig. Nashorn som en del av den nyeste Java 8-utgivelsen oversetter JavaScript dynamisk til bytekode slik at det kjører naturlig på JVM.

Resten av denne artikkelen forklarer hvordan du bygger isomorfe webapper med React på JVM ved å bruke Nashorn og Spring Boot for å forhåndsgjengi React-visninger på serveren. Eksempelkoden er vert på GitHub og fokuserer på den offisielle React.js-opplæringen - et kommentarfelteksempel med støtte for nedmerking. Hvis du ennå ikke er kjent med React.js, følg bare trinnene beskrevet her. Det kan også være lurt å lese Nashorn Tutorial senere, men det er ikke obligatorisk for dette blogginnlegget.

Reagervisninger og maler

React-hovedkomponenten som beskrevet i den offisielle opplæringen ser slik ut:

var CommentBox = React.createClass({
    // ...
    render: function () {
        return (
            <div className="commentBox">
                <h1>Comments</h1>
                <CommentList data={this.state.data} />
                <CommentForm onCommentSubmit={this.handleCommentSubmit} />
            </div>
        );
    }
});

For å gjengi denne komponenten i nettleseren definerer vi en funksjon renderClient som ganske enkelt kaller React.render for å legge ved visningen til innholdsbeholderen på siden:

var renderClient = function (data) {
    React.render(
        <CommentBox data={data} url='comments.json' pollInterval={5000} />,
        document.getElementById("content")
    );
};

Å kalle denne funksjonen gjengir kommentarfeltkomponenten til et forhåndsdefinert innhold div container ved å sende en rekke kommentarobjekter som data. Vi kan ikke kalle denne funksjonen på serveren fordi den i stor grad avhenger av at nettleserens DOM er tilstede (se document ).

Den store delen om React.render er at den respekterer forhåndsgjengitt HTML fra serveren:

Hvis du ringer React.render() på en node som allerede har denne server-gjengitte markeringen, vil React bevare den og bare legge ved hendelsesbehandlere, slik at du kan få en svært effektiv første-last-opplevelse.

Så neste trinn er å gjengi hele visningen på serveren. Vi trenger to ting for å oppnå det:(i) den forhåndsrenderte html-en og (ii) de forhåndsbehandlede JSON-dataene som input for renderClient .

La oss definere en mal index.jsp med modellattributter content og data . Innhold vil bli fylt med den forhåndsrenderte HTML-en i kommentarfeltet mens data erstattes med JSON-utvalget av kommentarer, slik at React kan initialisere alle komponentrekvisitter og -tilstander ved sideinnlasting.

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Hello React</title>
    <script type="text/javascript" src="react.js"></script>
    <script type="text/javascript" src="showdown.js"></script>
    <script type="text/javascript" src="jquery.js"></script>
</head>
<body>
<div id="content">${content}</div>
<script type="text/javascript" src="commentBox.js"></script>
<script type="text/javascript">
    $(function () {
        renderClient(${data});
    });
</script>
</body>
</html>

Du kan sikkert velge et annet malbibliotek enn JSP, f.eks. Thymeleaf eller styre. Uansett hva du foretrekker...

Gengivelse på serversiden med Nashorn

Java 8 leveres med en helt ny JavaScript-motor kalt Nashorn. Du kan ganske enkelt lage en ny Nashorn-motor ved å bruke ScriptEngineManager .

NashornScriptEngine nashorn = (NashornScriptEngine)
    new ScriptEngineManager().getEngineByName("nashorn");
nashorn.eval(read("nashorn-polyfill.js"));
nashorn.eval(read("react.js"));
nashorn.eval(read("showdown.js"));
nashorn.eval(read("commentBox.js"));

Koden ovenfor evaluerer alle skript som trengs for opplæringen i kommentarfeltet. Hjelpemetoden read leser ganske enkelt en fil fra klassebanen ved å lage en ny io-leser:

Reader read(String path) {
    InputStream in = getClass().getClassLoader().getResourceAsStream(path);
    return new InputStreamReader(in);
}

Dessverre React evaluerer ikke riktig på Nashorn uten noen rettelser. Jeg har opprettet en fil som heter nashorn-polyfill.js for å løse disse problemene (se dette problemet).

Dette er innholdet i denne filen:

var global = this;

var console = {};
console.debug = print;
console.warn = print;
console.log = print;

Følgende Java-metode demonstrerer hvordan du gjengir HTML-en til kommentarfeltet-opplæringen via Nashorn på serveren:

String renderCommentBox(List<Comment> comments) {
    try {

        Object html = nashorn.invokeFunction("renderServer", comments);
        return String.valueOf(html);
    }
    catch (Exception e) {
        throw new IllegalStateException("failed to render react component", e);
    }
}

Som du kan se sender vi direkte en innebygd Java-liste med kommentarer som inndata. Vi kaller JavaScript-funksjonen renderServer ligger i commentBox.js . Det ser ganske likt ut som renderClient som beskrevet ovenfor:

var renderServer = function (comments) {
    var data = Java.from(comments);
    return React.renderToString(
        <CommentBox data={data} url='comments.json' pollInterval={5000} />
    );
};

Funksjonen renderServer godtar en innebygd Java-liste med kommentarer som argument. Siden React-komponentene implementert i commentBox.js forventer en javascript-matrise, må vi konvertere java-listen til den tilsvarende javascript-matrisen ved å ringe Java.from . Funksjonen React.renderToString oppretter til slutt ønsket visning og returnerer en HTML-streng.

Hovedkontrolleren

Til slutt pakker vi alt inn i en Spring Boot-kontroller. Husk at vi trenger begge modellattributtene content og data for å gjengi index.jsp på riktig måte . Vi har nettopp lært å generere HTML-innhold med Nashorn. Men vi trenger også de første JSON-dataene slik at React vet om komponentens rekvisitter og tilstander. Dette er ganske enkelt ved å bruke Jacksons ObjectMapper for å konvertere listen over kommentarer til de riktige JSON-dataene.

@Controller
class MainController {
    CommentService service;
    React react;
    ObjectMapper mapper;

    @Autowired
    MainController(CommentService service) {
        this.service = service;
        this.react = new React();
        this.mapper = new ObjectMapper();
    }

    @RequestMapping("/")
    String index(Map<String, Object> model) throws Exception {
        List<Comment> comments = service.getComments();
        String content = react.renderCommentBox(comments);
        String data = mapper.writeValueAsString(comments);
        model.put("content", content);
        model.put("data", data);
        return "index";
    }
}

Det er alt. Hovedkontrolleren gjengir HTML-en for alle tilgjengelige kommentarer på serveren. React.render kalles opp i nettleseren ved sideinnlasting, bevarer all server-gjengitt markering, initialiserer de interne rekvisittene og tilstandene til komponentene og registrerer alle hendelsesbehandlere. Kommentarboksen oppdateres automatisk hvert par sekunder, og nyopprettede kommentarer vil bli knyttet direkte til DOM uten å vente på at serveren skal svare.

Isomorf servergjengivelse i dette eksemplet har mange fordeler sammenlignet med å generere visningene utelukkende på klienten:Siden er fullstendig søkemotoroptimalisert (SEO), slik at søkemotorer som Google kan analysere hver kommentar riktig. Det er heller ingen UI-flimmer ved første sideinnlasting:Uten servergjengivelse viser nettleseren først en tom side, henter deretter alle kommentarer og gjengir markeringen. Avhengig av ytelsen til klientens maskinvare vil du merke noe flimring ved oppstart. Dette er enda mer et problem på mobile enheter.

Jeg håper du likte å lese dette innlegget. Hvis du vil lære mer om Nashorn-motoren, vil du sannsynligvis lese min Nashorn-veiledning. Den fullstendige kildekoden til dette eksempelprosjektet er vert på GitHub. Fordel depotet eller send meg tilbakemeldingen din via Twitter.


Java Tag