Capítulo 17: Framework Nuxt 3 (Universal Rendering e File-based Routing)

Especialização em Front-End com Vue • Vue 3 & Nuxt 3 • Composition API, Pinia, Vue Router, SSR e TypeScript


🗺️ Mapa Conceitual do Framework Nuxt 3

flowchart TD
    A["Requisição do Navegador / Cliente"] --> B["Servidor Nitro Engine (H3 + Node/Edge)"]

    subgraph SSR_PIPELINE["Pipeline de Renderização Universal (SSR)"]
        C["1. Execução de Server Middleware"]
        D["2. Resolução de Rota Baseada em Arquivos (pages/)"]
        E["3. Execução de useAsyncData / $fetch no Servidor"]
        F["4. Serialização do Estado em window.__NUXT__"]
        G["5. Envio do HTML Pré-Renderizado para o Cliente"]
    end

    B --> SSR_PIPELINE
    G --> H["Navegador do Usuário: Exibição Imediata (First Paint)"]
    H --> I["Hidratação do Vue 3: Ativação dos Eventos Reativos no Cliente"]

    style A fill:#e1f5fe,stroke:#03a9f4,stroke-width:2px
    style B fill:#fff3e0,stroke:#ff9800,stroke-width:2px
    style SSR_PIPELINE fill:#ede7f6,stroke:#7e57c2,stroke-width:2px
    style I fill:#e8f5e9,stroke:#4caf50,stroke-width:2px

🏛️ 1. A Revolução do Nuxt 3: SSR, Nitro e Convenções

O Nuxt 3 é o framework full-stack oficial de maior prestígio construído sobre o Vue 3. Ele combina o poder do Vite para compilação client-side ultra-rápida com o novo motor de servidor Nitro Engine, viabilizando renderização isomórfica em qualquer plataforma de nuvem (Node.js, AWS Lambda, Cloudflare Workers, Vercel ou Netlify).

1.1 Universal Rendering (SSR) e Hidratação Isomórfica

No modelo clássico de SPA pura (Vite client-side), o servidor entrega um arquivo HTML praticamente vazio (<div id="app"></div>) e o navegador precisa baixar e executar todo o JavaScript para que o usuário veja a página. No Nuxt 3 com Universal Rendering:

  1. O servidor (Nitro) executa os componentes Vue e as chamadas assíncronas de dados, enviando ao navegador o HTML completo e totalmente preenchido.
  2. O usuário enxerga o conteúdo imediatamente (minimizando o FCP - First Contentful Paint e otimizando o SEO).
  3. O JavaScript do cliente é carregado em segundo plano e realiza a Hidratação (Hydration): anexa ouvintes de eventos e reatividade ao HTML estático sem piscar ou recriar a tela.

1.2 Roteamento Baseado em Arquivos (pages/)

O Nuxt analisa a árvore de diretórios dentro de pages/ e gera automaticamente a tabela de rotas do Vue Router:

  • pages/index.vue ➔ /
  • pages/projetos/index.vue ➔ /projetos
  • pages/projetos/[id].vue ➔ /projetos/:id (Rotas Dinâmicas)
  • pages/[...slug].vue ➔ Rotas Catch-All (ex: 404 customizado)

1.3 Sistema de Auto-Imports

No Nuxt 3, você não precisa declarar dezenas de imports manuais no topo dos arquivos .vue:

  • Todas as funções da Composition API (ref, computed, watch, onMounted) são auto-importadas.
  • Todos os composables customizados criados na pasta composables/ são auto-importados.
  • Todos os componentes criados na pasta components/ são auto-importados com suporte a lazy-loading automático (<LazyCard />).

1.4 Consumo Universal de Dados: useAsyncData e useFetch

Para evitar o problema crônico de Double Fetching (onde o servidor faz a requisição e o cliente faz a mesma requisição novamente após a hidratação):

// Executa no servidor e injeta o resultado no payload serializado
const { data, pending, error } = await useFetch('/api/usuarios');

⚖️ 2. Tabela Comparativa: Vite SPA Pura vs Nuxt 3

Critério Arquitetural Vite SPA Pura Nuxt 3 (Universal Framework)
SEO e Indexação de Buscadores Baixo a Nulo sem pré-renderização externa. Perfeito (HTML entregue pré-renderizado).
Primeira Pintura (FCP) Lenta (aguarda download de todo o bundle JS). Instantânea (HTML puro recebido de imediato).
Configuração de Rotas Manual em router/index.ts. Automática baseada em arquivos (pages/).
Motor de Backend Embutido Inexistente (requer backend externo separado). Nativo com Nitro (server/api/).
Complexidade de Deploy Simples (qualquer CDN ou S3). Simples a Média (Node server, Serverless ou Edge).

⚠️ 3. Armadilhas Comuns em Produção (Gotchas & Anti-Patterns)

Anti-Pattern 1: Erros de Hydration Mismatch

Erro Comum: Escrever código no template que renderiza valores diferentes no servidor e no cliente, como exibir a data corrente (new Date().toLocaleTimeString()) ou conferir window.innerWidth. Consequência: O console exibirá o aviso [Hydration mismatch], forçando o Vue a descartar o HTML do servidor e re-renderizar todo o nó no cliente, degradando a performance. Abordagem Correta: Utilize <ClientOnly> para envolver partes da interface que só fazem sentido no navegador do usuário.

Anti-Pattern 2: Usar fetch() Nativo sem useAsyncData

Erro Comum: Chamar fetch('...') solto em <script setup>. O servidor executará a requisição e, logo em seguida, o navegador executará exatamente a mesma requisição no cliente, dobrando a carga sobre a API.


💻 4. Código Conceitual Arquitetural Comentado

<!-- pages/cluster/[id].vue -->
<script setup lang="ts">
// Nuxt 3 auto-importa useRoute, useAsyncData, useHead
const route = useRoute();
const clusterId = route.params.id as string;

interface ClusterInfo {
  id: string;
  name: string;
  region: string;
  nodesOnline: number;
}

// useAsyncData previne refetch no cliente reaproveitando o payload serializado
const { data: cluster, pending, error } = await useAsyncData<ClusterInfo>(
  `cluster-detail-${clusterId}`,
  () => $fetch(`/api/clusters/${clusterId}`)
);

// Injeção dinâmica de metadados para motores de busca e crawlers
useHead({
  title: () => `${cluster.value?.name ?? 'Cluster'} | Nuxt Enterprise`,
  meta: [
    { name: 'description', content: `Painel de telemetria do cluster ${clusterId}` }
  ]
});
</script>

<template>
  <div class="max-w-2xl mx-auto p-6 space-y-4">
    <div v-if="pending" class="p-6 bg-slate-50 border rounded-xl text-center text-xs text-slate-400">
      Carregando detalhes do cluster via SSR...
    </div>

    <div v-else-if="cluster" class="bg-white border rounded-2xl p-6 shadow-sm space-y-3">
      <div class="flex justify-between items-center">
        <h2 class="text-xl font-black text-slate-800">{{ cluster.name }}</h2>
        <span class="text-xs px-2.5 py-1 bg-emerald-100 text-emerald-800 rounded-full font-bold">
          {{ cluster.region }}
        </span>
      </div>
      <p class="text-xs text-slate-500 font-mono">ID do Cluster: {{ cluster.id }}</p>
      <div class="text-lg font-bold text-indigo-600">
        {{ cluster.nodesOnline }} nós operacionais
      </div>
    </div>
  </div>
</template>

🛠️ 5. Cheat-Sheet de Diretórios Canônicos do Nuxt 3

meu-projeto-nuxt/
├── server/          # Rotas de API backend com Nitro (server/api/...)
├── pages/           # Rotas baseadas em arquivos (geram Vue Router)
├── layouts/         # Layouts reutilizáveis (default.vue, admin.vue)
├── components/      # Componentes com auto-import e prefixo de pasta
├── composables/     # Funções de lógica de estado com auto-import
├── stores/          # Stores globais do Pinia
├── public/          # Arquivos estáticos servidos na raiz (favicon, robots.txt)
└── nuxt.config.ts   # Arquivo de configuração central do Nuxt