Como construir aplicações SSR com React, React Router e Vite
No mundo do desenvolvimento web, duas abordagens reinam: SPAs (single page applications) que renderizam no lado do cliente e SSR (server side rendering) que renderiza páginas no servidor.
Com o SSR em ascensão, frameworks como Next.js e Remix conquistaram popularidade.
Mas e se você deseja migrar seu projeto React para SSR sem se prender a um framework específico?
É aí que entra o poder do Vite e do plugin Vike!
Como iniciar a aplicação
Bom, pra configurar uma aplicação usando Vite, mas do que fazer SSR, nós vamos usar um plugin chamado “Vike” (o nome antigo dele era vite-plugin-ssr e você pode ainda ver algumas referências antigas a ele por esse nome). Bora lá?
Abra o seu terminal favorito e execute o comando:
npm init [email protected]
Repare que eu deixei explícita a versão, para que você possa tirar o maior proveito desse conteúdo. O resultado vai ser algo assim:
Need to install the following packages:
[email protected]
Ok to proceed? (y) y
✔ Project name: · the-office
Scaffolding project in /Users/vinny/Desktop/the-office...
✔ Select a boilerplate: · react
Done. Now run:
cd the-office/
npm install (or pnpm install / yarn install)
npm run dev (or pnpm run dev / yarn dev)
Para o Project name eu respondi the-office
e para Select a boilerplate
eu selecionei react.
Agora podemos acessar a pasta recém-criada e instalar as dependências conforme sugerido:
cd the-office
E depois:
npm install
Por fim, levantamos o servidor de desenvolvimento executando:
npm run dev
E ao acessar http://localhost:3000/, teremos o seguinte resultado na tela:
A estrutura de pastas do projeto ficará assim:
├── package-lock.json
├── package.json
├── pages
│ ├── about
│ │ ├── code.css
│ │ └── index.page.jsx
│ └── index
│ ├── Counter.jsx
│ └── index.page.jsx
├── renderer
│ ├── Link.jsx
│ ├── PageShell.css
│ ├── PageShell.jsx
│ ├── PropTypeValues.js
│ ├── _default.page.client.jsx
│ ├── _default.page.server.jsx
│ ├── _error.page.jsx
│ ├── logo.svg
│ └── usePageContext.jsx
├── server
│ ├── index.js
│ └── root.js
└── vite.config.js
E agora que deu tudo certo na inicialização do nosso projeto, vamos entender o que tem nele.
Como analisar o projeto
Pasta pages
A pasta pages é o coração do sistema de roteamento do Vike. Cada subpasta ou arquivo dentro dela representa uma rota na aplicação.
O Vike utiliza um sistema de roteamento baseado em arquivos, o que significa que a estrutura de pastas e arquivos dentro de pages mapeia diretamente para as URLs da sua aplicação.
Vamos entender cada uma delas:
about
: esta subpasta contém os arquivos relacionados à página 'sobre' (/about);code.css
: um arquivo de estilo CSS específico para a página 'sobre';index.page.jsx
: o componente React que é renderizado quando os usuários acessam/about
no navegador.
index
: esta subpasta representa a raiz do site ou a página inicial;Counter.jsx
: um exemplo de componente React;index.page.jsx
: o componente React que é renderizado na raiz do site (/).
Pasta renderer
A pasta renderer contém arquivos que definem como as páginas são processadas e renderizadas, tanto no lado do servidor quanto no cliente.
Essa pasta pode conter componentes globais, estilos, e lógicas de renderização padrão. Dentro dela encontramos:
Link.jsx
: um componente para criar links de navegação entre páginas, respeitando o roteamento do lado do cliente;PageShell.css
ePageShell.jsx
: definem um "shell" ou layout comum, que podem ser utilizados em todas as páginas para manter a consistência no estilo e na estrutura;PropTypeValues.js
: define valores comuns de PropTypes reutilizados em vários componentes;_default.page.client.jsx
e_default.page.server.jsx
: componentes especiais que definem comportamentos padrão de renderização no lado do cliente e servidor, respectivamente;_error.page.jsx
: um componente que é renderizado quando ocorre um erro durante a navegação ou a renderização de uma página;usePageContext.jsx
: um hook personalizado para acessar o contexto da página atual, útil para passar dados e estados entre o servidor e o cliente.
Pasta server
A pasta server contém arquivos que configuram e inicializam o servidor da aplicação.
Essa configuração inclui o roteamento no servidor, a integração com APIs, e a renderização inicial das páginas no lado do servidor. Nela temos:
index.js
: o ponto de entrada (entry point) do servidor, responsável por inicializar e configurar o servidor Vike;root.js
: um arquivo que pode ser usado para definir rotas de servidor personalizadas, middleware, ou para configurar a integração com sistemas externos.
Roteamento
No Vike o roteamento é, principalmente, gerenciado pelo sistema de arquivos dentro da pasta pages.
O nome de cada arquivo ou subpasta define a rota correspondente na aplicação. Arquivos especiais, como _default.page.client.jsx
e _default.page.server.jsx
, permitem definir comportamentos padrão de renderização que são aplicados a todas as páginas.
A navegação entre as páginas é feita sem recarregar a página inteira, graças ao roteamento do lado do cliente implementado no Link.jsx
e outros mecanismos do próprio Vike.
Server Routing ou Client Routing?
O Vike oferece suporte tanto para o Server Routing (recarregamento completo do HTML ao navegar pela página) quanto para o Client Routing (alterações/hidratações no DOM ao navegar pela página).
O guia Server Routing VS Client Routing ajuda a escolher qual usar e quando usar cada uma das opções.
Route Strings & Route Functions
Por padrão, o Vike trabalha com Filesystem Routing:
Também podemos definir as chamadas "Route Strings" and "Route Functions".
// /pages/product/+route.js
// Esse arquivo implementa a roda do path `/pages/product/+Page.js`
// Route String
export default '/product/@productId'
O parâmetro
productId
está disponível empageContext.routeParams.productId
.
As Route Functions nos dão total flexibilidade para implementar lógicas de roteamento avançadas, por exemplo, guardas de rota:
// /pages/product/edit/+route.js
// Este arquivo define a rota de /pages/product/edit/+Page.js
import { resolveRoute } from 'vike/routing'
export default async (pageContext) => {
// Garantir que /product/@id/edit possa ser acessado apenas por administradores
if (!pageContext.user.isAdmin) {
return false
}
// Podemos usar o resolvedor de String de Rota do Vike
return resolveRoute('/product/@id/edit', pageContext.urlPathname)
}
Como obter dados
Existem várias formas de obter dados em aplicações que usam o Vike, vamos destacar as mais utilizadas: data()
e useData()
:
data()
A forma mais simples de buscar dados é usar o hook
data() do Vike e o hook
useData() para obter dentro de componentes. Veja o exemplo a seguir:
// /pages/movies/@id/+data.js
// Ambiente: servidor
export { data }
import fetch from 'node-fetch'
async function data(pageContext) {
const { id } = pageContext.routeParams
const response = await fetch(`https://star-wars.brillout.com/api/films/${id}.json`)
let filme = await response.json()
// `filme` é serializado e passado para o cliente. Portanto, escolhemos apenas os
// dados que o cliente precisa para minimizar o que é enviado pela rede.
filme = { title: filme.title, release_date: filme.release_date }
// data() roda apenas no lado do servidor por padrão, podemos, portanto, usar consultas ORM/SQL.
/* Com um ORM:
const filmes = await Movie.findAll({ select: ['title', 'release_date'] }) */
/* Com SQL:
const filmes = await sql.run('SELECT { title, release_date } FROM movies;') */
return {
filmes
}
}
O @id no caminho
/pages/movie/@id/+data.js
indica um parâmetro de rota, e o seu valor fica disponível dentro dopageContext.routeParams.id
(veja a documentação de Guias > Roteamento para mais informações).
useData()
import React from 'react';
import { useData } from 'vike-react/useData';
function TodoList() {
// Supondo que `useData()` retorne os dados necessários para o componente, incluindo a lista de tarefas
const { todos } = useData();
return (
<div>
<h1>Lista de Tarefas</h1>
<ul>
{todos.map(todo => (
<li key={todo.id} style={{ textDecoration: todo.completed ? 'line-through' : 'none' }}>
{todo.title}
</li>
))}
</ul>
</div>
);
}
export default TodoList;
useData()
possui pacotes específicos para cada framework/biblioteca (vike-react no nosso caso).
O hook data()
é utilizado apenas para buscar os dados iniciais de uma página (os dados SSR).
Outras opções
O Vike já possui suporte para:
Alternativas ao Vike
Ao escolher uma ferramenta para desenvolver aplicações web, o panorama inclui opções como Vike, Remix, Next.js, Gatsby e a combinação de React com React Router.
Cada uma tem suas características, projetadas para atender a diferentes necessidades e preferências de desenvolvimento.
Vike se destaca por sua flexibilidade e abordagem inovadora com suporte a múltiplos frameworks, como Vue, React e Svelte.
Ideal para quem busca otimizações específicas de desempenho e está aberto a explorar novas metodologias, como a ideia de ilhas, que carrega apenas os componentes necessários, melhorando o tempo de carregamento.
Remix, por outro lado, foca na renderização no servidor e no carregamento progressivo de dados, priorizando uma experiência da pessoa usuária suave desde o carregamento inicial.
É uma ótima escolha para aplicações que dependem fortemente de dados dinâmicos e querem otimizar a primeira interação do usuário.
Next.js já é bem estabelecido entre desenvolvedores React, oferecendo uma solução completa com renderização estática e do lado do servidor.
Sua vasta comunidade e ecossistema o tornam ideal para quem busca uma solução robusta e comprovada, especialmente para projetos que se beneficiam de SEO e otimizações automáticas.
Gatsby se sobressai na geração de sites estáticos, sendo perfeito para sites que não mudam frequentemente, como blogs e portfólios.
Com otimizações prontas para SEO e performance, Gatsby é a escolha certa para projetos focados em velocidade de carregamento e conteúdo estático.
Por fim, a combinação de React e React Router é a velha conhecida para criar SPAs.
Enquanto permite um controle granular sobre a experiência do usuário e o roteamento, pode não oferecer as mesmas otimizações de desempenho do Vike.
Essa escolha é ideal para quem já está confortável com React e busca uma solução confiável e bem testada para aplicações dinâmicas.
Conclusão
A decisão entre Vike e as outras opções deve ser baseadas nas especificidades do projeto, como necessidades de desempenho, SEO, dinamicidade do conteúdo e familiaridade da equipe com as tecnologias.
Cada ferramenta tem seu lugar, cabendo a você decidir qual se encaixa melhor no contexto do seu projeto.
Se você quiser mergulhar nesse mundo de performance e otimização, super recomendo a formação React: maximizando a performance de Aplicações, em que o instrutor Pedro compartilha várias técnicas que ele usa no dia a dia de trabalho.
Referências
- Vike: https://vike.dev/
- Remix: https://remix.run/
- Next.js: https://nextjs.org/
- Gatsby: https://www.gatsbyjs.com/