No novo ano, vamos começar nossa conversa com você com um artigo inicial sobre renderização do lado do servidor. Se você estiver interessado, uma publicação mais recente sobre Nuxt.js e mais trabalhos de publicação nessa direção são possíveis.
Com o advento de bibliotecas e estruturas JavaScript modernas, que se destinam principalmente à criação de páginas da web interativas e aplicativos de página única, todo o processo de mostrar páginas ao usuário mudou muito.
Antes do advento de aplicativos inteiramente gerados por JS no navegador, o HTML era servido ao cliente em resposta a uma chamada HTTP. Isso pode ser feito retornando um arquivo HTML estático com o conteúdo ou processando a resposta usando uma linguagem do lado do servidor (PHP, Python ou Java) e de uma forma mais dinâmica.
Esta solução permite criar sites responsivos que rodam muito mais rápido do que os sites padrão de solicitação-resposta, pois elimina o tempo gasto pela solicitação "no caminho".
Uma resposta típica de um servidor a uma solicitação a um site escrito em React seria mais ou menos assim:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<link rel="shortcut icon" href="/favicon.ico">
<title>React App</title>
</head>
<body>
<div id="root"></div>
<script src="/app.js"></script>
</body>
</html>
Ao selecionar esta resposta, nosso navegador também selecionará o "pacote" que
app.js
contém nosso aplicativo e, após um ou dois segundos, renderizará a página inteira.
Neste ponto, você já pode usar o inspetor HTML integrado do navegador para visualizar todo o HTML renderizado. No entanto, olhando para o código-fonte, não veremos nada além do HTML acima.
Por que isso é um problema?
Embora esse comportamento não seja um problema para a maioria de nossos usuários ou durante o desenvolvimento de um aplicativo, pode se tornar indesejável se:
- -, ,
- , ,
Se, do ponto de vista demográfico, o seu público-alvo pertence a um desses grupos, trabalhar com o site será inconveniente - em particular, os usuários terão que esperar muito tempo, olhando para a placa “Carregando ...” (ou pior, para uma tela em branco).
“Ok, mas demograficamente, meu público-alvo definitivamente não é um desses grupos, então devo me preocupar?”
Há duas outras coisas a serem consideradas ao trabalhar com um aplicativo renderizado pelo cliente: mecanismos de pesquisa e presença na mídia social .
Hoje, de todos os motores de busca, apenas o Google tem alguma capacidade de exibir um site e levar em consideração seu JS antes de exibir uma página. Além disso, embora o Google consiga exibir a página de índice do seu site, sabe-se que pode haver problemas para navegar em sites que possuem um roteador.
Isso significa que será muito difícil para o seu site chegar ao topo dos resultados de qualquer mecanismo de pesquisa que não seja o Google.
O mesmo problema é visto nas redes sociais, por exemplo, no Facebook - se um link para o seu site for compartilhado, nem o nome nem a imagem de visualização serão exibidos corretamente.
Como resolver este problema
Existem várias maneiras de resolver isso.
R - Tente manter todas as páginas principais do seu site estáticas
Quando é criado um site de plataforma, onde o usuário terá que fazer login com seu nome de usuário, e sem fazer login no sistema, o conteúdo não é fornecido ao visitante, você pode tentar deixar páginas públicas estáticas (escritas em HTML) de seu site, em particular, o índice, "sobre nós", "contatos "E não use JS ao exibi-los .
Como seu conteúdo é limitado por requisitos de login, ele não será indexado por mecanismos de pesquisa e não pode ser compartilhado nas redes sociais.
B - Gere partes de seu aplicativo como páginas HTML durante a construção
Você pode adicionar bibliotecas como react-snapshot ao seu projeto ; eles são usados para gerar cópias HTML das páginas de seu aplicativo e armazená-las em um diretório dedicado. Este diretório é então implantado junto com o pacote JS. Assim, o HTML será veiculado a partir do servidor junto com a resposta, e seu site também será visto por aqueles usuários que têm o JavaScript desabilitado, bem como pelos motores de busca, etc.
Como regra, configurar o react-snapshot não é difícil: basta adicionar a biblioteca ao seu projeto e alterar o script de construção da seguinte maneira:
"build": "webpack && react-snapshot --build-dir static"
A desvantagem desta solução é a seguinte: todo o conteúdo que queremos gerar deve estar disponível em tempo de construção - não podemos acessar nenhuma API para obtê-lo, também não podemos pré-gerar conteúdo que dependa dos dados fornecidos pelo usuário (por exemplo, de um URL).
C - Crie um aplicativo JS que usa renderização de servidor
Um dos maiores pontos de venda da geração atual de aplicativos JS é que eles podem ser executados no cliente (navegador) e no servidor. Isso possibilita a geração de HTML para páginas mais dinâmicas, cujo conteúdo ainda não é conhecido no momento da construção. Essas aplicações são freqüentemente chamadas de "isomórficas" ou "universais".
As duas soluções de renderização do lado do servidor mais populares para React são:
- next.js - github.com/zeit/next.js
- Gatsby - github.com/gatsbyjs/gatsby
Crie sua própria implementação de SSR
Importante: se você for tentar criar sua própria implementação SSR para aplicativos React, precisará fornecer um back-end de nó para seu servidor. Você não será capaz de implantar esta solução em um host estático como faria com as páginas do github.
A primeira coisa que precisamos fazer é criar um aplicativo, como qualquer outro aplicativo React.
Vamos criar um ponto de entrada:
// index.js
import React from 'react';
import { render } from 'react-dom';
import App from './App.js';render(<App />, document.getElementById('root'));
E o aplicativo-componente (App):
// App.js
import React from 'react';const App = () => {
return (
<div>
Welcome to SSR powered React application!
</div>
);
}
E também um "wrapper" para carregar nosso aplicativo:
// index.html
<!doctype html>
<html>
<head>
<meta charset="utf-8" />
</head>
<body>
<div id="root"></div>
<script src="/bundle.js"></script>
</body>
</html>
Como você pode ver, o aplicativo é bastante simples. Neste artigo, não passaremos por todas as etapas necessárias para gerar o assembly webpack + babel correto.
Se você iniciar o aplicativo em seu estado atual, uma mensagem de boas-vindas aparecerá na tela. Olhando o código-fonte, você verá o conteúdo do arquivo
index.html
, mas a mensagem de boas-vindas não estará lá. Para resolver este problema, vamos adicionar a renderização do servidor. Primeiro, vamos adicionar 3 pacotes:
yarn add express pug babel-node --save-dev
Express é um servidor da web poderoso para node, pug é um mecanismo de modelagem que pode ser usado com express e babel-node é um wrapper para node que fornece transpilação instantânea.
Primeiro, vamos copiar nosso arquivo
index.html
e salvá-lo como
index.pug
:
// index.pug <!doctype html> <html> <head> <meta charset="utf-8" /> </head> <body> <div id="root">!{app}</div> <script src="bundle.js"></script> </body> </html>
Como você pode ver, o arquivo não mudou muito, exceto pelo que agora está inserido no HTML
!{app}
. Esta é uma variável
pug
que mais tarde será substituída pelo HTML real.
Vamos criar nosso servidor:
// server.jsimport React from 'react';
import { renderToString } from 'react-dom/server';
import express from 'express';
import path from 'path';import App from './src/App';const app = express();
app.set('view engine', 'pug');
app.use('/', express.static(path.join(__dirname, 'dist')));app.get('*', (req, res) => {
const html = renderToString(
<App />
); res.render(path.join(__dirname, 'src/index.pug'), {
app: html
});
});app.listen(3000, () => console.log('listening on port 3000'));
Vamos analisar este arquivo em ordem.
import { renderToString } from 'react-dom/server';
A biblioteca react-dom contém uma exportação nomeada separada
renderToString
que funciona como a que conhecemos
render
, mas não renderiza o DOM, mas o HTML como uma string.
const app = express();
app.set('view engine', 'pug');
app.use('/', express.static(path.join(__dirname, 'dist')));
Criamos uma nova instância de servidor expresso e informamos que usaremos um mecanismo de modelagem
pug
. Nesse caso, poderíamos continuar com a leitura normal do arquivo e executar uma operação "localizar e substituir", mas essa abordagem é realmente ineficaz e pode causar problemas devido ao acesso múltiplo ao sistema de arquivos ou problemas com o cache.
Na última linha, dizemos ao express para procurar um arquivo em um diretório
dist
e, se a solicitação (por exemplo
/bundle.js
) corresponder a um arquivo presente nesse diretório, retorne-o.
app.get('*', (req, res) => {
});
Agora pedimos ao express para adicionar um manipulador a cada URL não correspondente - incluindo nosso arquivo inexistente
index.html
(como você se lembra, nós o renomeamos para
index.pug
, e ele não está no diretório
dist
).
const html = renderToString(
<App />
);
Com a ajuda
renderToString
, exibimos nosso aplicativo. O código se parece exatamente com o ponto de entrada, mas essa correspondência é opcional.
res.render(path.join(__dirname, 'src/index.pug'), {
app: html
});
Agora que renderizamos o HTML, pedimos ao express para renderizar o arquivo em resposta
index.pug
e substituir a variável
app
pelo HTML que recebemos.
app.listen(3000, () => console.log('listening on port 3000'));
Por fim, garantimos que o servidor seja inicializado e configurado para que ele escute na porta 3000.
Agora, só precisamos adicionar o script necessário para
package.json
:
"scripts": {
"server": "babel-node server.js"
}
Agora, ao chamar
yarn run server
, devemos receber a confirmação de que o servidor está realmente funcionando. Vá para localhost : 3000 no navegador , onde, novamente, devemos ver nosso aplicativo. Se olharmos para o código-fonte nesta fase, vemos:
<!doctype html>
<html>
<head>
<meta charset="utf-8" />
</head>
<body>
<div id="root">div data-reactroot="">Welcome to SSR powered React application!</div></div>
<script src="bundle.js"></script>
</body>
</html>
Se tudo estiver assim, significa que a renderização do servidor está funcionando conforme o esperado e você pode começar a estender seu aplicativo!
Por que ainda precisamos de bundle.js?
No caso de um aplicativo extremamente simples, que é considerado aqui, não é necessário incluir bundle.js - sem este arquivo, nosso aplicativo ainda funcionará. Mas, no caso de um aplicativo real, você ainda precisará incluir esse arquivo.
Isso permitirá que navegadores que podem lidar com JavaScript assumam o trabalho e interajam com sua página já no lado do cliente, e aqueles que não sabem como analisar JS irão para a página com o HTML necessário que o servidor retornou.
Coisas para lembrar
Apesar do fato de que a renderização do servidor parece bastante simples, ao desenvolver aplicativos, você precisa prestar atenção a alguns tópicos que à primeira vista não são muito óbvios:
- , , . , , HTML,
this.state
, componentDidMount
— , , . , , . , (res.render
) , . -- react (. @reach/router react-router) , URL, . !