📚 Módulo 06: Frontend - Setup do React 18, Vite e Lazy Routing
Neste módulo, daremos início ao desenvolvimento do Frontend da TecLoja 03 (Repositório 2). Inicializaremos a nossa SPA utilizando o empacotador de alta performance Vite configurado com TypeScript, definiremos as interfaces de dados unificadas correspondentes aos DTOs do nosso backend e configuraremos as rotas lógicas da aplicação com o React Router utilizando divisão de código dinâmico (Lazy Loading) e Suspense.
🗺️ Client-Side Routing (SPA)
Diferente de sites tradicionais onde cada clique carrega uma nova página do servidor, a SPA captura o clique localmente e renderiza o componente necessário em tempo real.
flowchart LR
classDef react fill:#61dafb,stroke:#20232a,stroke-width:2px,color:#000;
A(Navegador URL) --> B{React Router}:::react
B -- Rota / --> C[Renderiza Componente Catalogo]:::react
B -- Rota /carrinho --> D[Renderiza Componente Carrinho]:::react
B -- Rota Inexistente --> E[Redireciona para /]:::react
🛠️ 1. Inicializando o Projeto React com Vite e TypeScript
Entre em um diretório fora da pasta do seu backend para criarmos o repositório independente do frontend. Execute os comandos a seguir no seu terminal:
# 1. Gerar o projeto React 18 + TypeScript via Vite de forma automática
npm create vite@latest tecloja-frontend -- --template react-ts
# 2. Entrar na pasta do projeto
cd tecloja-frontend
# 3. Instalar as dependências essenciais de navegação e conexão
npm install react-router-dom axios
# 4. Instalar tipos adicionais e utilitários de desenvolvimento
npm install @types/node --save-dev
📄 2. Definindo as Interfaces de Tipos (TypeScript DTOs)
Para manter a consistência de comunicação entre React e a API NestJS, precisamos definir no frontend os contratos de tipos correspondentes aos dados trafegados nas requisições REST.
Crie um arquivo centralizador de tipos em src/types/index.ts:
// src/types/index.ts
export interface Categoria {
id: number;
nome: string;
}
export interface Produto {
id: number;
nome: string;
descricao?: string;
preco: number;
estoque: number;
categoriaId: number;
categoria?: Categoria;
}
export interface UsuarioLogado {
id: number;
username: string;
role: 'ADMIN' | 'USER';
token: string;
}
export interface ItemPedidoInput {
produtoId: number;
quantidade: number;
}
export interface CheckoutInput {
clienteId: number;
itens: ItemPedidoInput[];
}
export interface ItemPedidoResponse {
id: number;
produtoId: number;
quantidade: number;
precoUnitario: number;
produto: Produto;
}
export interface PedidoResponse {
id: number;
dataCriacao: string;
status: string;
valorTotal: number;
clienteId: number;
itens: ItemPedidoResponse[];
cliente: {
id: number;
nome: string;
email: string;
};
}
🗺️ 3. Roteamento Dinâmico com React Router e Lazy Loading
No desenvolvimento de Single Page Applications (SPA), carregar todos os componentes do sistema em uma única requisição inicial degrada severamente a experiência do usuário devido ao tamanho do arquivo final.
Para otimizar o carregamento, utilizaremos as APIs nativas do React:
lazy(): Importa os componentes de página de forma assíncrona apenas quando a rota for acessada fisicamente.Suspense: Exibe um feedback visual de carregamento (Loading) provisório enquanto o navegador baixa o arquivo JS do componente.
Abra o arquivo de inicialização do frontend src/App.tsx e configure a malha de rotas conforme abaixo:
// src/App.tsx
import React, { lazy, Suspense } from 'react';
import { BrowserRouter, Routes, Route, Navigate } from 'react-router-dom';
import Navbar from './components/Navbar';
// Carregamento dinâmico e assíncrono (Lazy Loading) das páginas
const Catalogo = lazy(() => import('./views/Catalogo'));
const Carrinho = lazy(() => import('./views/Carrinho'));
const Login = lazy(() => import('./views/Login'));
const AdminProdutos = lazy(() => import('./views/admin/AdminProdutos'));
const ProdutoForm = lazy(() => import('./views/admin/ProdutoForm'));
function App() {
return (
<BrowserRouter>
{/* Barra de navegação global visível em todas as rotas */}
<Navbar />
<main className="container-principal">
<Suspense fallback={<div className="loading-fallback">Carregando módulo de tela...</div>}>
<Routes>
{/* Rotas Públicas */}
<Route path="/" element={<Catalogo />} />
<Route path="/carrinho" element={<Carrinho />} />
<Route path="/login" element={<Login />} />
{/* Rotas Administrativas Protegidas */}
<Route path="/admin/produtos" element={<AdminProdutos />} />
<Route path="/admin/produtos/novo" element={<ProdutoForm />} />
<Route path="/admin/produtos/editar/:id" element={<ProdutoForm />} />
{/* Redirecionamento de rotas inexistentes */}
<Route path="*" element={<Navigate to="/" replace />} />
</Routes>
</Suspense>
</main>
</BrowserRouter>
);
}
export default App;
✅ Pré-Requisitos deste Módulo
Antes de passar para o desenvolvimento de lógicas de autenticação, axios interceptors e estilos, certifique-se de que:
- A API REST do backend NestJS foi finalizada nos módulos anteriores e está pronta para receber conexões HTTP.
- O ambiente de desenvolvimento local possui o Node.js (v18 ou superior) instalado.
🤔 Por que fizemos assim?
- Por que inicializar o React com Vite em vez de usar o clássico Create React App (CRA)? O Vite utiliza empacotamento nativo via módulos ES (ESM) em tempo de desenvolvimento. Isso significa que ele delega o carregamento de arquivos para o próprio navegador e utiliza o transpilador esbuild (escrito em Go) para processar os arquivos TypeScript de forma quase instantânea. O Create React App utiliza Webpack sob o capô, exigindo que toda a aplicação seja transpilada e empacotada em memória antes de iniciar o servidor de testes, gerando lentidão extrema conforme o projeto cresce.
- Por que usar importações dinâmicas (
lazy()) combinadas com o componente<Suspense>? Em Single Page Applications, se não usarmos técnicas de otimização, o Vite gera um único arquivo JavaScript massivo contendo todo o código do sistema (incluindo rotas administrativas e painéis complexos). Isso degrada drasticamente a performance inicial da loja. Olazy()implementa a divisão de código (Code-Splitting), dividindo o build em blocos menores (chunks). O código da área administrativa só é baixado pelo navegador do cliente quando ele de fato clica para acessar uma rota/admin. O componente<Suspense>atua capturando o estado de carregamento e exibindo uma interface temporária amigável enquanto o download do arquivo de rota é concluído. - Por que mapear DTOs do backend em Interfaces estritas no TypeScript no Frontend? Garante a coesão absoluta de dados entre cliente e servidor. Caso a equipe de backend modifique a tipagem de um preço de decimal para string ou altere o nome de um campo no DTO, o compilador do TypeScript no frontend detecta a inconsistência em tempo de build antes de enviar o app para produção, evitando erros em tempo de execução como o clássico
TypeError: Cannot read properties of undefined.
🔍 Checkpoint
- Dependências Instaladas: Verifique se as bibliotecas
react-router-domeaxiosforam catalogadas e salvas na seçãodependenciesdo arquivopackage.jsonno frontend. - Servidor de Desenvolvimento Rodando: Execute
npm run devno terminal do frontend e acesse o endereço fornecido (normalmentehttp://localhost:5173). Garanta que a página inicial carrega perfeitamente e nenhuma falha crítica de roteamento ou compilação é exibida no painel. - Contrato de Roteamento: Tente digitar manualmente caminhos públicos na URL do navegador como
/ou/logine verifique se o React Router faz a transição das views sem forçar uma requisição de página completa para o servidor (sem piscar a aba do navegador).
⚠️ Erros Comuns
| Erro | Causa | Solução |
|---|---|---|
npm ERR! code ENOENT ao tentar executar comandos no terminal do projeto |
O comando foi executado fora da pasta de contexto raiz do repositório frontend. | Certifique-se de navegar para a pasta correspondente executando cd tecloja-frontend antes de rodar os scripts do npm. |
Tela branca total no navegador com erro React code crashed: A component suspended while responding to synchronous input... |
Os componentes importados via lazy() foram renderizados fora do escopo de proteção do elemento <Suspense>. |
Verifique se as rotas internas mapeadas estão devidamente encapsuladas pelo elemento <Suspense fallback={...}> no arquivo src/App.tsx. |
| O TypeScript reclama que não pode resolver importações relativas de views ou componentes | O caminho relativo fornecido para a chamada do import dinâmico na configuração do roteador possui erros de sintaxe ou letras. | Lembre-se de que caminhos relativos dependem da localização exata do arquivo. No src/App.tsx, uma pasta admin deve ser acessada via ./views/admin/... respeitando as maiúsculas e minúsculas física das pastas. |
🏁 Conclusão
Com a estrutura de rotas dinâmicas do React inicializada e as tipagens mapeadas com sucesso, temos a base para criar o motor de comunicação assíncrona.
No Módulo 07, criaremos o Contexto de Autenticação Reativa (AuthContext), programaremos os Interceptadores do Axios para anexar automaticamente o cabeçalho de token de forma transparente e projetaremos um Design System Responsivo de alta fidelidade utilizando os conceitos estéticos modernos de glassmorphism em CSS puro.