404 Not Found

404 Not Found


nginx

Module Development

Charlie descobriu que a lógica de rastreamento, relatório de erros e monitoramento de performance do MegaShop estava espalhada por vários locais. Os outros projetos de Alice e Bob também precisavam das mesmas capacidades. Ao encapsular essas funcionalidades comuns em módulos Nuxt, eles poderiam desenvolvê-las uma vez e reutilizá-las em todos os lugares, e até publicá-las no npm para a comunidade usar.

1. O Que Você Vai Aprender


2. Uma História Real de um Arquiteto

(1) Dor: Desenvolvimento Duplicado de Funcionalidades Genéricas

O MegaShop do Charlie precisa de rastreamento—cada visualização de página, cada item adicionado ao carrinho e cada clique deve ser registrado. Ele chama a API manualmente em cinco componentes. O outro projeto de Alice também precisa de rastreamento, então Bob reescreveu o código do zero. O código é repetitivo e inconsistente.

(2) Solução com Módulos Nuxt

Uma vez empacotado como módulo, está pronto para uso imediato—com injeção automática de Composable e API do servidor:

TYPESCRIPT
// nuxt.config.ts
modules: ['@megashop/analytics']

(3) Benefícios: Desenvolva Uma Vez, Reutilize em Todos os Lugares

Todos os projetos ganham capacidades de rastreamento simplesmente instalando o módulo. Com Alice, você pode habilitar o rastreamento em qualquer projeto chamando useAnalytics()—sem configuração necessária.


3. Arquitetura de Módulos

(1) Ciclo de Vida do Módulo Nuxt

100%
graph TB
    A[defineNuxtModule] --> B[Setup Function]
    B --> C[installModule - Dependências]
    B --> D[addPlugin - Registrar Plugins]
    B --> E[addComposable - Injetar Composables]
    B --> F[addServerHandler - Rotas de API]
    B --> G[addLayout - Layouts Customizados]
    B --> H[addComponent - Auto Componentes]
    B --> I[extendConfig - Modificar Config]
    
    J[Nuxt Hooks] --> K[modules:before]
    J --> L[modules:done]
    J --> M[build:before]
    J --> N[build:done]

(1) ▶ Exemplo: Esqueleto de Módulo Mínimo

TYPESCRIPT
// src/module.ts
import { defineNuxtModule, addPlugin, createResolver } from '@nuxt/kit'

export default defineNuxtModule({
  meta: {
    name: '@megashop/analytics',
    configKey: 'analytics',
    compatibility: {
      nuxt: '^3.0.0'
    }
  },

  defaults: {
    enabled: true,
    endpoint: '/api/analytics',
    debug: false
  },

  setup(options, nuxt) {
    const { resolve } = createResolver(import.meta.url)

    // Registrar plugin
    addPlugin(resolve('./runtime/plugin'))

    // Expor opções para o runtime
    nuxt.options.runtimeConfig.public.analytics = {
      enabled: options.enabled,
      endpoint: options.endpoint,
      debug: options.debug
    }
  }
})

Saída:

TEXT
// Execução bem-sucedida

(2) Referência Rápida de Capacidades do Módulo

Capacidade API Descrição
Registrar Plugin addPlugin() Executar automaticamente lógica de inicialização
Injetar componente addComponent() Auto-importar componentes Vue
Injetar Composable addImports() Auto-importar funções
Adicionar Rota de API addServerHandler() Registrar automaticamente rotas do servidor
Adicionar Layout addLayout() Registrar layout customizado
Adicionar middleware addRouteMiddleware() Registrar middleware de rota
Modificar Configuração extendConfig() Modificar Configuração do Nuxt
Instalar módulo dependência installModule() Instalar outros módulos

4. Módulo Prático: @megashop/analytics

(1) ▶ Exemplo: Ponto de Entrada do Módulo

TYPESCRIPT
// src/module.ts
import { defineNuxtModule, addPlugin, addImports, addServerHandler, createResolver } from '@nuxt/kit'

export interface ModuleOptions {
  enabled: boolean
  endpoint: string
  debug: boolean
  trackPageViews: boolean
  trackClicks: boolean
}

export default defineNuxtModule<ModuleOptions>({
  meta: {
    name: '@megashop/analytics',
    configKey: 'analytics',
    compatibility: { nuxt: '^3.0.0' }
  },

  defaults: {
    enabled: true,
    endpoint: '/api/analytics',
    debug: false,
    trackPageViews: true,
    trackClicks: true
  },

  setup(options, nuxt) {
    const { resolve } = createResolver(import.meta.url)

    // 1. Registrar plugin client para auto-tracking
    if (options.trackPageViews || options.trackClicks) {
      addPlugin(resolve('./runtime/plugin.client'))
    }

    // 2. Auto-importar composable useAnalytics
    addImports({
      name: 'useAnalytics',
      from: resolve('./runtime/composables/useAnalytics')
    })

    // 3. Adicionar API do servidor para receber eventos
    addServerHandler({
      method: 'post',
      route: options.endpoint,
      handler: resolve('./runtime/server/api/analytics')
    })

    // 4. Expor config para o runtime
    nuxt.options.runtimeConfig.public.analytics = {
      enabled: options.enabled,
      endpoint: options.endpoint,
      debug: options.debug
    }
  }
})

Saída:

TEXT
// Execução bem-sucedida

(2) ▶ Exemplo: Plugin de Runtime

TYPESCRIPT
// src/runtime/plugin.client.ts
import { defineNuxtPlugin } from '#app'

export default defineNuxtPlugin((nuxtApp) => {
  const config = useRuntimeConfig().public.analytics
  if (!config.enabled) return

  // Auto-rastrear visualizações de página
  if (config.trackPageViews) {
    nuxtApp.hook('page:finish', () => {
      useAnalytics().trackPageView(window.location.pathname)
    })
  }

  // Auto-rastrear cliques em elementos data-track
  if (config.trackClicks) {
    document.addEventListener('click', (e) => {
      const target = (e.target as HTMLElement).closest('[data-track]')
      if (target) {
        const event = target.getAttribute('data-track') || 'click'
        useAnalytics().track(event, { element: target.tagName })
      }
    })
  }
})

Saída:

TEXT
// Execução bem-sucedida

(3) ▶ Exemplo: Composable de Runtime

TYPESCRIPT
// src/runtime/composables/useAnalytics.ts
export function useAnalytics() {
  const config = useRuntimeConfig().public.analytics

  async function track(event: string, data?: Record<string, any>) {
    if (!config.enabled) return
    if (config.debug) console.log('[Analytics]', event, data)

    await $fetch(config.endpoint, {
      method: 'POST',
      body: { event, data, timestamp: Date.now(), url: import.meta.client ? window.location.href : '' }
    })
  }

  function trackPageView(path: string) {
    track('page_view', { path })
  }

  function trackAddToCart(productId: number, productName: string, price: number) {
    track('add_to_cart', { productId, productName, price, currency: 'USD' })
  }

  function trackPurchase(orderId: string, total: number) {
    track('purchase', { orderId, total, currency: 'USD' })
  }

  return { track, trackPageView, trackAddToCart, trackPurchase }
}

Saída:

TEXT
// Execução bem-sucedida

(4) ▶ Exemplo: API do Servidor de Runtime

TYPESCRIPT
// src/runtime/server/api/analytics.ts
import { defineEventHandler, readBody, setHeader } from 'h3'

export default defineEventHandler(async (event) => {
  const body = await readBody(event)

  // Validar campos obrigatórios
  if (!body.event) {
    throw createError({ statusCode: 400, message: 'Event name required' })
  }

  // Armazenar evento (em produção: enviar para serviço de analytics)
  const storage = useStorage('analytics')
  const key = `event:${Date.now()}:${Math.random().toString(36).slice(2)}`
  await storage.setItem(key, {
    event: body.event,
    data: body.data || {},
    timestamp: body.timestamp || Date.now(),
    url: body.url,
    userAgent: getHeader(event, 'user-agent')
  })

  setHeader(event, 'cache-control', 'no-store')
  return { success: true }
})

Saída:

TEXT
// Execução bem-sucedida

5. Publicação de Módulo

(1) ▶ Exemplo: Configuração do package.json

JSON
{
  "name": "@megashop/analytics",
  "version": "1.0.0",
  "type": "module",
  "main": "./dist/module.mjs",
  "types": "./dist/types.d.ts",
  "exports": {
    ".": {
      "import": "./dist/module.mjs",
      "require": "./dist/module.cjs",
      "types": "./dist/types.d.ts"
    },
    "./runtime/*": "./dist/runtime/*"
  },
  "files": ["dist"],
  "scripts": {
    "build": "nuxt-module-build",
    "dev": "nuxt-module-build --stub",
    "test": "vitest run",
    "prepublishOnly": "npm run build"
  },
  "peerDependencies": {
    "nuxt": "^3.0.0"
  },
  "devDependencies": {
    "@nuxt/module-builder": "^0.6.0",
    "@nuxt/test-utils": "^3.0.0",
    "nuxt": "^3.12.0"
  }
}

Saída:

JSON
{
  "name": "@megashop/analytics",
  "version": "1.0.0",
  "type": "module",
  "main": "./dist/module.mjs",
  "types": "./dist/types.d.ts",
  "exports": {
    ".": {
      "import": "./dist/module.mjs",
      "require": "./dist/module.cjs",
      "types": "./dist/types.d.ts"
    },
    "./runtime/*": "./dist/runtime/*"
  },
  "files": [
    "dist"
  ],
  "scripts": {
    "build": "nuxt-module-build",
    "dev": "nuxt-module-build --stub",
    "test": "vitest run",
    "prepublishOnly": "npm run build"
  }
}

(2) ▶ Exemplo: Usando em um projeto

TYPESCRIPT
// nuxt.config.ts do MegaShop
export default defineNuxtConfig({
  modules: [
    // Módulo local durante desenvolvimento
    '~/modules/analytics',
    // Módulo publicado em produção
    // '@megashop/analytics'
  ],

  analytics: {
    enabled: true,
    endpoint: '/api/analytics',
    debug: process.env.NODE_ENV === 'development',
    trackPageViews: true,
    trackClicks: true
  }
})

Saída:

TEXT
// Execução bem-sucedida

6. Testes de Módulo

(1) ▶ Exemplo: Fixture de Teste do Módulo

TYPESCRIPT
// test/module.test.ts
import { setupTest } from '@nuxt/test-utils'

describe('@megashop/analytics module', () => {
  setupTest({
    fixture: './test/fixtures/basic',
    build: true
  })

  test('registers analytics plugin', () => {
    // Plugin auto-registra, verificar plugins do Nuxt
    const nuxt = useNuxt()
    const hasPlugin = nuxt.options.plugins.some(p => p.src?.includes('analytics'))
    expect(hasPlugin).toBe(true)
  })

  test('exposes useAnalytics composable', async () => {
    // Auto-import disponível
    const { data } = await useFetch('/api/analytics', {
      method: 'POST',
      body: { event: 'test', data: {} }
    })
    expect(data.value).toBeDefined()
  })

  test('analytics endpoint accepts events', async () => {
    const response = await $fetch('/api/analytics', {
      method: 'POST',
      body: { event: 'page_view', data: { path: '/' } }
    })
    expect(response.success).toBe(true)
  })
})

Saída:

TEXT
// Execução bem-sucedida

7. Exemplo Completo: Usando @megashop/analytics

VUE
<!-- pages/products/[id].vue - Usando módulo de analytics -->
<template>
  <div v-if="product">
    <h1>{{ product.name }}</h1>
    <button
      @click="handleAddToCart"
      data-track="add_to_cart"
    >
      Add to Cart
    </button>
  </div>
</template>

<script setup lang="ts">
const route = useRoute()
const { data: product } = await useFetch(`/api/products/${route.params.id}`)

// useAnalytics auto-importado pelo módulo
const { trackAddToCart, trackPageView } = useAnalytics()

// Rastreamento manual
onMounted(() => {
  trackPageView(`/products/${route.params.id}`)
})

async function handleAddToCart() {
  if (!product.value) return
  trackAddToCart(product.value.id, product.value.name, product.value.price)
  // ... lógica de adicionar ao carrinho
}
</script>

❓ Perguntas Frequentes

P Qual é a diferença entre módulos e plugins?
R Módulos são executados durante a fase de build (fase de configuração) e podem injetar plugins, componentes, Composables e rotas. Plugins são executados em runtime (quando o app inicia) e lidam com lógica de inicialização. Módulos podem conter plugins.
P O que é o diretório runtime no módulo?
R runtime contém código de runtime—código que é executado apenas quando a aplicação está rodando e não é incluído na configuração de build. Plugins, Composables e server handlers são todos colocados sob runtime/ e não são processados pelo Nuxt Kit.
P Como desenvolver e depurar módulos locais?
R Crie um módulo no diretório modules/ do projeto e referencie-o em nuxt.config.ts como ~/modules/xxx. Durante o desenvolvimento, use nuxt-module-build --stub para gerar links simbólicos; alterações de código terão efeito em tempo real.
P Um módulo pode depender de outros módulos?
R Sim. Use installModule('@pinia/nuxt') no setup para instalar módulos dependentes. O Nuxt removerá duplicatas automaticamente.
P O que preciso para publicar no npm?
R Configure exports e types no package.json, construa usando @nuxt/module-builder, e execute npm publish. Recomendamos usar GitHub Actions para publicação automatizada.
P Como exportar tipos TypeScript para um módulo?
R @nuxt/module-builder gera automaticamente declarações de tipos. Configure exports.types no package.json para apontar para dist/types.d.ts, e os usuários receberão dicas de tipos automaticamente após a instalação.

📖 Resumo


📝 Exercícios

  1. Exercício Básico (Dificuldade: ⭐): Crie um esqueleto de módulo mínimo, registre um plugin e exiba "Module loaded"
  2. Exercício Avançado (Dificuldade: ⭐⭐): Desenvolva o módulo @megashop/analytics para implementar o Composable useAnalytics e a API do servidor
  3. Desafio (Dificuldade: ⭐⭐⭐): Adicione rastreamento automático de visualização de página e rastreamento de clique data-track, depois escreva testes unitários para verificar a funcionalidade

---|

Web-Tutorial.com

Equipe Técnica Web-Tutorial

Uma plataforma de tutoriais mantida por diversos desenvolvedores. Cada tutorial é escrito e revisado por profissionais da área correspondente. Trabalhamos para manter nosso conteúdo preciso e confiável — se encontrar algum problema, avise-nos.

100%