Como construir aplicações SSR com React, React Router e Vite

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!

É um GIF do personagem Dwight Schrute, interpretado por Rainn Wilson, na série de televisão americana "The Office". Ele está em um ambiente de escritório, com persianas nas janelas ao fundo, e tem uma expressão de suspeita ou confusão enquanto olha para a câmera. Ele está vestido com uma camisa social e uma gravata, comuns ao seu estilo característico no show. A legenda diz: “bring it on”.

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 imagem mostra uma página da web com um layout simples. À esquerda, há duas abas nomeadas "Home" e "About". No centro da página, há um título grande que diz "Welcome". Abaixo do título, há um texto que descreve a página: "This page is: Rendered to HTML. Interactive. Counter 0". O estilo visual sugere uma página de internet básica, possivelmente de um projeto web ou de um website em desenvolvimento. O contador implica uma funcionalidade interativa, como um botão que, ao ser clicado, incrementa o número exibido.

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.

Banner de divulgação da Imersão IA da Alura em colaboração com o Google. Mergulhe em Inteligência artificial com a Alura e o Google. Serão cinco aulas gratuitas para você aprender a usar IA na prática e desenvolver habilidades essenciais para o mercado de trabalho. Inscreva-se gratuitamente agora!

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 e PageShell.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:

A tela mostra uma correspondência entre o sistema de arquivos e URLs em um framework de desenvolvimento web. No lado esquerdo, sob "FILESYSTEM", há três caminhos: "pages/about+Page.js" mapeia para "/about", "pages/faq+Page.js" para "/faq", e "pages/index+Page.js" que é mapeado para a string vazia, indicando a raiz do site "/". Há também uma rota parametrizada, "pages/movie/@id+Page.js", que pode corresponder a várias URLs como "/movie/1", "/movie/2", "/movie/3", etc. O rodapé da imagem contém uma linha de barras e o logo "alura".

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 em pageContext.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 do pageContext.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

Vinicios Neves
Vinicios Neves

Vinicios é engenheiro de software, envolvido na arquitetura, design e implementação de microsserviços, micro frontends e sistemas distribuídos. Tem experiência significativas em aplicativos, integração e arquitetura corporativa. É Engenheiro de Software pela UNESA e Arquiteto de Software pela PUC Minas.

Veja outros artigos sobre Front-end