Aplicação isomórfica com React e Nashorn
1. Visão geral
2. Um pouco de história
Tradicionalmente, os aplicativos cliente e servidor eram escritos de uma maneira bastante pesada no lado do servidor. Pense no PHP como um mecanismo de script que gera principalmente HTML estático e navegadores da Web que os renderizam.
O Netscape veio comsupport of JavaScript in its browser way back in mid-nineties. Isso começou a mudar parte do processamento do navegador do lado do servidor para o lado do cliente. Por um longo tempo, os desenvolvedores enfrentaram diferentes problemas relacionados ao suporte a JavaScript em navegadores da web.
Com a crescente demanda por uma experiência do usuário mais rápida e interativa, o limite já estava sendo ultrapassado. One of the earliest frameworks that changed the game was jQuery. Ele trouxe várias funções amigáveis e suporte muito aprimorado para AJAX.
Logo, muitos frameworks para desenvolvimento de front-end começaram a aparecer, o que melhorou muito a experiência do desenvolvedor. Começando comAngularJS do Google, React do Facebook e, posteriormente,Vue,, eles começaram a chamar a atenção do desenvolvedor.
Com suporte de navegador moderno, estruturas notáveis e ferramentas necessárias,the tides are largely shifting towards client-side.
Uma experiência imersiva em dispositivos portáteis cada vez mais rápidos exige mais processamento do lado do cliente.
3. O que é um aplicativo isomórfico?
Então, vimos como as estruturas front-end estão nos ajudando a desenvolver um aplicativo da Web em que a interface do usuário é completamente renderizada no lado do cliente.
No entanto,it’s also possible to use the same framework at the server-sidee geram a mesma interface de usuário.
Agora, não precisamos seguir apenas soluções do lado do cliente ou apenas do servidor. A melhor maneira é ter uma solução ondethe client and server can both process the same front-end codee gerar a mesma interface de usuário.
Há benefícios nessa abordagem, que discutiremos mais tarde.
Such web applications are called Isomorphic or Universal. Agora, o idioma do cliente é mais exclusivamente JavaScript. Portanto, para que um aplicativo isomórfico funcione, também precisamos usar o JavaScript no servidor.
Node.js is by far the most common choice to build a server-side rendered application.
4. O que é Nashorn?
Então, onde Nashorn se encaixa e por que devemos usá-lo? Nashorn éa JavaScript engine packaged by default with Java. Portanto, se já temos um back-end de aplicativo da web em Java e queremos construir um aplicativo isomórfico,Nashorn is pretty handy!
Nashorn foi lançado como parte do Java 8. Isso se concentra principalmente em permitir aplicativos JavaScript incorporados em Java.
Nashorncompiles JavaScript in-memory to Java Bytecodee passa para a JVM para execução. Isso oferece melhor desempenho em comparação com o mecanismo anterior, Rhino.
5. Criando um aplicativo isomórfico
Já passamos por contexto suficiente agora. Nossa aplicação aqui exibirá uma sequência de Fibonacci e fornecerá um botão para gerar e exibir o próximo número na sequência. Vamos criar um aplicativo isomórfico simples agora com back-end e front-end:
-
Front-end: um front-end simples baseado em React.js.
-
Back-end: um back-end simples do Spring Boot com Nashorn para processar JavaScript
6. Aplicativo Front-End
Estaremos usandoReact.js for creating our front end. React é uma biblioteca JavaScript popular para a criação de aplicativos de página única. Éhelps us decompose a complex user interface into hierarchical components com estado opcional e ligação de dados unilateral.
React analisa essa hierarquia e cria uma estrutura de dados na memória chamada DOM virtual. Isso ajuda o React a encontrar alterações entre diferentes estados e a fazer alterações mínimas no DOM do navegador.
6.1. React Component
Vamos criar nosso primeiro componente React:
var App = React.createClass({displayName: "App",
handleSubmit: function() {
var last = this.state.data[this.state.data.length-1];
var secondLast = this.state.data[this.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(this)
});
},
componentDidMount: function() {
this.setState({data: this.props.data});
},
getInitialState: function() {
return {data: []};
},
render: function() {
return (
React.createElement("div", {className: "app"},
React.createElement("h2", null, "Fibonacci Generator"),
React.createElement("h2", null, this.state.data.toString()),
React.createElement("input", {type: "submit", value: "Next", onClick: this.handleSubmit})
)
);
}
});
Agora, vamos entender o que o código acima está fazendo:
-
Para começar, definimos um componente de classe no React chamado "App"
-
A maioria dosimportant function inside this component is “render”, que é responsável por gerar a interface do usuário
-
Fornecemos um estiloclassName que o componente pode usar
-
Estamos usando o estado do componente aqui para armazenar e exibir a série
-
Enquanto o estado é inicializado como uma lista vazia, ele busca dados passados para o componente como suporte quando o componente é montado
-
Finalmente, ao clicar no botão "Adicionar", é feita uma chamada do jQuery ao serviço REST
-
A chamada busca o próximo número na sequência e o anexa ao estado do componente
-
A mudança no estado do componente renderiza novamente o componente automaticamente
6.2. Usando o componente React
React procuraa named “div” element in the HTML page to anchor its contents. Tudo o que precisamos fazer é fornecer uma página HTML com esse elemento "div" e carregar os arquivos JS:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
Hello React
Então, vamos ver o que fizemos aqui:
-
Nósimported the required JS libraries, react, react-dom and jQuery
-
Depois disso, definimos um elemento "div" chamado "root"
-
Também importamos o arquivo JS com nosso componente React
-
Em seguida, chamamos o componente React de "App" com alguns dados iniciais, os três primeiros números de Fibonacci
7. Back-end do aplicativo
Agora, vamos ver como podemos criar um back-end adequado para nosso aplicativo. Já decidimosuse Spring Boot along with Spring Web para construir este aplicativo. Mais importante, decidimosuse Nashorn to process the JavaScript-based front-end que desenvolvemos na última seção.
7.1. Dependências do Maven
Para nosso aplicativo simples, usaremos JSP junto com Spring MVC, portanto, adicionaremos algumas dependências ao nosso POM:
org.springframework.boot
spring-boot-starter-web
org.apache.tomcat.embed
tomcat-embed-jasper
provided
O primeiro é ostandard spring boot dependency para um aplicativo da web. O segundo éneeded to compile JSPs.
7.2. Controlador da Web
Vamos agora criar nosso controlador da web, que irá processar nosso arquivo JavaScript e retornar um HTML usando JSP:
@Controller
public class MyWebController {
@RequestMapping("/")
public String index(Map model) throws Exception {
ScriptEngine nashorn = new ScriptEngineManager().getEngineByName("nashorn");
nashorn.eval(new FileReader("static/js/react.js"));
nashorn.eval(new FileReader("static/js/react-dom-server.js"));
nashorn.eval(new FileReader("static/app.js"));
Object html = nashorn.eval(
"ReactDOMServer.renderToString(" +
"React.createElement(App, {data: [0,1,1]})" +
");");
model.put("content", String.valueOf(html));
return "index";
}
}
Então, o que exatamente está acontecendo aqui:
-
Buscamos uma instância deScriptEngine do tipo Nashorn deScriptEngineManager
-
Então, nósload relevant libraries to React, react.js, and react-dom-server.js
-
Também carregamos nosso arquivo JS que possui nosso componente de reação "App"
-
Por fim, avaliamos um fragmento JS criando um elemento de reação com o componente "App" e alguns dados iniciais
-
Isso nos fornece uma saída de React, um fragmento de HTML comoObject
-
Passamos esse fragmento HTML como dados para a visualização relevante - o JSP
7.3. JSP
Agora, como processamos esse fragmento HTML em nosso JSP?
Lembre-se de que o React adiciona automaticamente sua saída a um elemento chamado "div" - "root" no nosso caso. No entanto,we’ll add our server-side generated HTML fragment to the same element manually em nosso JSP.
Vamos ver como o JSP se parece agora:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
Hello React!
${content}
Esta é a mesma página que criamos anteriormente, exceto pelo fato de que adicionamos nosso fragmento HTML na div "raiz", que estava vazia anteriormente.
7.4. Controlador REST
Por fim, também precisamos de um terminal REST do lado do servidor que nos forneça o próximo número de Fibonacci na sequência:
@RestController
public class MyRestController {
@RequestMapping("/next/{last}/{secondLast}")
public int index(
@PathVariable("last") int last,
@PathVariable("secondLast") int secondLast) throws Exception {
return last + secondLast;
}
}
Nada sofisticado aqui, apenas um simples controlador Spring REST.
8. Executando o aplicativo
Agora que completamos nosso front-end, bem como nosso back-end, é hora de executar o aplicativo.
Deveríamos iniciar o aplicativo Spring Boot normalmente, usando a classe bootstrapping:
@SpringBootApplication
public class Application extends SpringBootServletInitializer {
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(Application.class);
}
public static void main(String[] args) throws Exception {
SpringApplication.run(Application.class, args);
}
}
Quando executamos esta classe,Spring Boot compiles our JSPs and makes them available on embedded Tomcat junto com o resto do aplicativo da web.
Agora, se visitarmos nosso site, veremos:
Vamos entender a sequência de eventos:
-
O navegador solicita esta página
-
Quando a solicitação para esta página chega, o Spring Web Controller processa os arquivos JS
-
O mecanismo Nashorn gera um fragmento HTML e o passa para o JSP
-
O JSP adiciona esse fragmento HTML ao elemento div "raiz", retornando finalmente a página HTML acima
-
O navegador renderiza o HTML e, enquanto isso, começa a baixar arquivos JS
-
Por fim, a página está pronta para ações do lado do cliente - podemos adicionar mais números na série
O importante a entender aqui é o que acontece se o React encontrar um fragmento HTML no elemento "div" de destino. Nesses casos,React compares this fragment with what it has and does not replace it if it finds a legible fragment. É exatamente isso que habilita a renderização no servidor e aplicativos isomórficos.
9. O que mais é possível?
Em nosso exemplo simples, apenas arranhamos a superfície do que é possível. Os aplicativos front-end com estruturas modernas baseadas em JS estão ficando cada vez mais poderosas e complexas. Com essa complexidade adicional, há muitas coisas que precisamos cuidar:
-
Criamos apenas um componente React em nosso aplicativo quando, na realidade, pode serseveral components forming a hierarchy que passa dados por meio de props
-
Gostaríamos decreate separate JS files for every component para mantê-los gerenciáveis e gerenciar suas dependências por meio de “exportar / exigir” ou “exportar / importar”
-
Além disso, pode não ser possível gerenciar o estado apenas nos componentes; podemos querer usara state management library like Redux
-
Além disso, podemos ter que interagir com serviços externos como efeitos colaterais das ações; isso pode exigir que usemosa pattern like redux-thunk or Redux-Saga
-
Mais importante, gostaríamos deleverage JSX, a syntax extension to JS para descrever a interface do usuário
Embora o Nashorn seja totalmente compatível com JS puro, ele pode não suportar todos os recursos mencionados acima. Muitos deles exigem compilação trans e polyfills devido à compatibilidade com JS.
A prática usual em tais casos éleverage a module bundler like Webpack or Rollup. O que eles fazem principalmente é processar todos os arquivos de origem do React e agrupá-los em um único arquivo JS junto com todas as dependências. Isso invariavelmente requer um compilador JavaScript moderno comoBabel para compilar o JavaScript para ser compatível com versões anteriores.
O pacote final possui apenas JS antigo e bom, que os navegadores podem entender e Nashorn também adota.
10. Benefícios de um aplicativo isomórfico
Então, falamos muito sobre aplicativos isomórficos e até criamos um aplicativo simples agora. Mas por que exatamente devemos nos preocupar com isso? Vamos entender alguns dos principais benefícios de usar um aplicativo isomórfico.
10.1. Renderização da primeira página
Um dos benefícios mais significativos de um aplicativo isomórfico éthe faster rendering of the first page. No aplicativo renderizado típico do lado do cliente, o navegador inicia o download de todos os artefatos JS e CSS.
Depois disso, eles carregam e começam a renderizar a primeira página. Se enviarmos a primeira página renderizada do lado do servidor, isso poderá ser muito mais rápido, proporcionando uma experiência aprimorada ao usuário.
10.2. SEO Friendly
Outrobenefit often cited with server-side rendering is related to SEO. Acredita-se que os bots de pesquisa não são capazes de processar JavaScript e, portanto, não veem uma página de índice renderizada no lado do cliente por meio de bibliotecas como React. Uma página renderizada no servidor, portanto, é mais amigável para o SEO. É importante notar, porém, que os bots de mecanismos de pesquisa modernos afirmam processar JavaScript.
11. Conclusão
Neste tutorial, analisamos os conceitos básicos de aplicativos isomórficos e o mecanismo JavaScript Nashorn. Exploramos ainda mais como criar um aplicativo isomórfico com Spring Boot, React e Nashorn.
Em seguida, discutimos as outras possibilidades de estender o aplicativo front-end e os benefícios do uso de um aplicativo isomórfico.
Como sempre, o código pode ser encontradoover on GitHub.