Arquitetura e Migração de Nuxt 2 para Nuxt 3: Guia Técnico

A transição do Nuxt 2 para o Nuxt 3 representa uma reescrita arquitetural completa, impulsionada pelo Vite, Vue 3 e o motor Nitro. Os benefícios práticos incluem bundles de produção significativamente menores, Hot Module Replacement (HMR) instantâneo no desenvolvimento, adoção nativa da Composition API, separação clara entre as camadas de cliente e servidor, e substituição do Vuex pelo Pinia.

Devido às mudanças profundas no core do framework, o comando npx nuxi upgrade não é recomendado. A abordagem correta e mais segura é inicializar um novo projeto Nuxt 3 e realizar a migração manual dos módulos, configurações e componentes.

Configurações e Estrutura de Diretórios

O arquivo nuxt.config.ts sofre alterações significativas. A sintaxe CommonJS (module.exports) é substituída por ES Modules (export default).

Reorganização de Diretórios e Metadados

É altamente recomendado isolar o código-fonte em um diretório dedicado, como application/, configurando o srcDir. As configurações globais de <head> foram movidas para o objeto app, e as configurações de ambiente foram unificadas no runtimeConfig.

// nuxt.config.ts
export default defineNuxtConfig({
  srcDir: 'application/',
  
  app: {
    head: {
      title: 'Minha Aplicação',
      charset: 'utf-8',
      viewport: 'width=device-width, initial-scale=1',
      meta: [
        { name: 'description', content: 'Aplicação migrada para Nuxt 3' }
      ]
    }
  },

  runtimeConfig: {
    apiSecret: process.env.API_SECRET,
    public: {
      apiBase: process.env.API_BASE_URL || '/api'
    }
  }
})

Proxy de Desenvolvimento com Nitro

O módulo @nuxtjs/proxy foi descontinuado. O Nuxt 3 utiliza o motor Nitro para gerenciar proxies. Note que o devProxy opera estritamente em ambiente de desenvolvimento.

// nuxt.config.ts
export default defineNuxtConfig({
  nitro: {
    devProxy: {
      '/graphql': {
        target: 'https://api.staging.internal',
        changeOrigin: true,
        headers: { 'X-Forwarded-Host': 'localhost' }
      }
    }
  }
})

Camada de Front-end: Layouts, Componentes e Composables

A estrutura de rotas e componentes permanece baseada no sistema de arquivos, mas a sintaxe dos componentes deve ser atualizada para o Vue 3.

Definição de Layouts

A definição de layouts agora é feita via macro definePageMeta dentro do bloco <script setup>.

<script setup>
definePageMeta({
  layout: 'admin-dashboard',
})
</script>

Assets e Arquivos Estáticos

O diretório assets/ continua destinado a arquivos processados pelo bundler (CSS, SCSS, imagens importadas). O diretório public/ serve arquivos estáticos brutos (como favicon.ico ou robots.txt) diretamente na raiz do servidor. Para importar imagens no JavaScript ou templates, utilize a sintaxe import em vez de require.

Composables (Auto-importação)

O diretório composables/ é nativo do Nuxt 3. Funções exportadas neste diretório são automaticamente injetadas no contexto da aplicação, eliminando a necessidade de imports explícitos em componentes, layouts ou plugins.

// application/composables/useCurrency.ts
type CurrencyFormatter = (value: number, locale: string) => string;

export const formatCurrency: CurrencyFormatter = (value, locale) => {
  return new Intl.NumberFormat(locale, {
    style: 'currency',
    currency: 'BRL'
  }).format(value);
};

Uso direto no componente:

<script setup>
const price = formatCurrency(1599.90, 'pt-BR');
</script>

Plugins e Controle de SSR

Em vez de confgiurar a execução SSR no nuxt.config, o Nuxt 3 utiliza sufixos de arquivo. Adicione .client.ts para executar apenas no cliente, ou .server.ts para apenas no servidor.

// application/plugins/leaflet.client.ts
import L from 'leaflet';

export default defineNuxtPlugin(() => {
  return {
    provide: {
      mapEngine: L
    }
  };
});

Gerenciamento de Estado com Pinia

O Vuex não é mais integrado. O Pinia é a biblioteac oficial, oferecendo uma API mais limpa, suporte nativo ao TypeScript e eliminação da distinção entre mutations e actions.

// application/stores/cart.ts
import { defineStore } from 'pinia';

interface CartItem {
  id: string;
  quantity: number;
}

export const useCartStore = defineStore('cart', {
  state: () => ({
    items: [] as CartItem[],
    discount: 0
  }),
  
  getters: {
    totalItems: (state) => state.items.reduce((sum, item) => sum + item.quantity, 0)
  },

  actions: {
    addProduct(productId: string) {
      const existing = this.items.find(i => i.id === productId);
      if (existing) {
        existing.quantity++;
      } else {
        this.items.push({ id: productId, quantity: 1 });
      }
    }
  }
});

Camada de Servidor e Motor Nitro

A maior mudança arquitetural é a introdução do diretório server/, que isola completamente a lógica de backend, rotas, middleware e plugins do Nitro.

Endpoints de API

Arquivos dentro de server/api/ expõem endpoints HTTP. O handler utiliza defineEventHandler.

// server/api/health.ts
export default defineEventHandler(() => {
  return {
    status: 'operational',
    timestamp: Date.now()
  };
});

Roteamento e Proxy Avançado

Para rotas que não se enquadram em /api, ou para criar proxies complexos, utilize o diretório server/routes/ combinado com as utilidades do pacote h3.

// server/routes/analytics/[...path].ts
import { sendProxy } from 'h3';

export default defineEventHandler(async (event) => {
  const targetBase = 'https://metrics.external-platform.com';
  const targetUrl = new URL(event.node.req.url || '', targetBase);
  
  return await sendProxy(event, targetUrl.toString());
});

Middleware de Servidor

O middleware em server/middleware/ intercepta todas as requisições antes de chegarem aos handlers. É ideal para injeção de headers, logging estruturado ou validação de tokens.

// server/middleware/requestLogger.ts
import crypto from 'crypto';

export default defineEventHandler((event) => {
  const requestId = crypto.randomUUID();
  
  event.node.res.setHeader('X-Request-Id', requestId);
  
  console.debug(`[${requestId}] ${event.method} ${event.path} - ${new Date().toISOString()}`);
});

Integração com $fetch e Variáveis de Ambiente

O Nuxt 3 desencoraja o uso direto do Axios no servidor, favorecendo a utilitária global $fetch (baseada no ofetch), que integra automaticamente com o runtimeConfig.

// server/api/users/index.ts
export default defineEventHandler(async (event) => {
  const config = useRuntimeConfig();
  
  const userData = await $fetch('/v1/users', {
    baseURL: config.public.apiBase,
    headers: {
      'Authorization': `Bearer ${config.apiSecret}`
    }
  });
  
  return userData;
});

Tags: nuxt3 vue3 pinia nitro-engine Vite

Publicado em 9-13 03:55