404 Not Found

404 Not Found


nginx

Sistema de Plugins

Charlie precisa integrar o SDK do Stripe Payments no MegaShop e também deseja um serviço global de logging. O Stripe é carregado apenas no lado do cliente, enquanto os logs são registrados no lado do servidor. Tradicionalmente, isso exigiria importar e inicializar o SDK manualmente em vários lugares, mas o sistema de plugins do Nuxt 3 automatiza todo o processo — registre uma vez, e ele fica disponível globalmente.

1. O Que Você Vai Aprender


2. Uma História Real de um Arquiteto

(1) Ponto Crítico: Inicialização de bibliotecas de terceiros espalhada pelo código

O MegaShop do Charlie precisa integrar pagamentos Stripe. Alice carregou o Stripe.js na página do carrinho de compras, e Bob carregou novamente na página de checkout — resultando em carregamentos duplicados — e em algumas páginas foi esquecido, causando indisponibilidade do botão de pagamento. O mesmo acontece com a ferramenta de logging; cada componente requer importação manual do logger.

(2) Solução com o Sistema de Plugins do Nuxt

Os plugins do Nuxt 3 são executados automaticamente e injetados via provide para uso global:

TYPESCRIPT
// plugins/stripe.client.ts
export default defineNuxtPlugin(() => {
  const stripe = Stripe(config.public.stripePublishableKey)
  return { provide: { stripe } }
})

(3) Benefícios: Registre uma vez para acesso global

O Stripe e o Logger são inicializados apenas uma vez dentro do plugin; todos os componentes os acessam via useNuxtApp().$stripe, eliminando a necessidade de carregamento duplicado ou importações manuais.


3. Registro Automático de Plugins

(1) Registro de Plugins e Momento de Execução

100%
sequenceDiagram
    participant N as Nuxt App
    participant P1 as 01-env.server.ts
    participant P2 as 02-stripe.client.ts
    participant P3 as 03-logger.ts
    participant C as Components

    N->>P1: Executar plugins do servidor primeiro
    N->>P2: Executar plugins do cliente na hidratação
    N->>P3: Executar plugins universais
    N->>C: Componentes podem usar $stripe, $logger

(2) Tipos de Plugins e Convenções de Nomenclatura

Nome Ambiente de Execução Propósito
plugins/xxx.ts SSR + Cliente Plugin de uso geral
plugins/xxx.server.ts Somente servidor Banco de dados/Chaves
plugins/xxx.client.ts Somente cliente SDK do navegador

(1) ▶Exemplo: Plugin de Logging de Uso Geral

TYPESCRIPT
// plugins/logger.ts
export default defineNuxtPlugin(() => {
  const logger = {
    info: (message: string, data?: any) => {
      console.log(`[INFO] ${message}`, data || '')
    },
    warn: (message: string, data?: any) => {
      console.warn(`[WARN] ${message}`, data || '')
    },
    error: (message: string, data?: any) => {
      console.error(`[ERROR] ${message}`, data || '')
    }
  }

  return {
    provide: {
      logger
    }
  }
})

Saída:

TEXT
// Execução Bem-sucedida

4. Explicação Detalhada do defineNuxtPlugin

(1) Mecanismo Provide/Inject

Papel API Descrição
Fornecido pelo Plugin provide: { xxx } Injeta $xxx no NuxtApp
Uso no Componente useNuxtApp().$xxx Acesso ao Serviço Injetado
Usando Composable useNuxtApp().$xxx Acessando Composable

(1) ▶Exemplo: Plugin de Pagamento Stripe (Lado do Cliente)

TYPESCRIPT
// plugins/stripe.client.ts
export default defineNuxtPlugin(() => {
  const config = useRuntimeConfig()
  const stripe = window.Stripe(config.public.stripePublishableKey)

  return {
    provide: {
      stripe
    }
  }
})

Saída:

TEXT
// Execução Bem-sucedida

(2) ▶Exemplo: Usando um plugin em um componente

VUE
<!-- pages/checkout.vue -->
<template>
  <div>
    <h1>Checkout - ${{ total }} USD</h1>
    <div ref="cardElement"></div>
    <button @click="processPayment" :disabled="processing">
      Pay ${{ total }} USD
    </button>
  </div>
</template>

<script setup lang="ts">
const { $stripe } = useNuxtApp()
const { $logger } = useNuxtApp()

const cart = useState<any[]>('cart')
const total = computed(() => cart.value.reduce((s, i) => s + i.price * i.quantity, 0))
const processing = ref(false)

async function processPayment() {
  processing.value = true
  try {
    $logger.info('Processing payment', { amount: total.value })
    // Usar Stripe para pagamento
    const result = await $stripe.confirmCardPayment('{PAYMENT_INTENT_SECRET}')
    if (result.error) {
      $logger.error('Payment failed', result.error)
    } else {
      $logger.info('Payment succeeded')
      navigateTo('/order/success')
    }
  } finally {
    processing.value = false
  }
}
</script>

Saída:

TEXT
// Execução Bem-sucedida

(3) ▶Exemplo: Plugin de Banco de Dados do Lado do Servidor

TYPESCRIPT
// plugins/database.server.ts
export default defineNuxtPlugin(() => {
  const config = useRuntimeConfig()

  // Executa apenas no servidor - conexão com banco de dados com chave privada
  const db = createDatabaseConnection(config.databaseUrl)

  return {
    provide: {
      db
    }
  }
})

Saída:

TEXT
// Execução Bem-sucedida

5. Distinguindo Entre Plugins do Lado do Servidor e do Cliente

(1) ▶Exemplo: Plugin de Notificação (Lado do cliente, usando APIs do navegador)

TYPESCRIPT
// plugins/notification.client.ts
export default defineNuxtPlugin(() => {
  function requestPermission() {
    if ('Notification' in window) {
      Notification.requestPermission()
    }
  }

  function send(title: string, body: string) {
    if (Notification.permission === 'granted') {
      new window.Notification(title, { body, icon: '/favicon.ico' })
    }
  }

  // Solicitar permissão automaticamente na primeira visita
  requestPermission()

  return { provide: { notification: { send, requestPermission } } }
})

Saída:

TEXT
// Execução Bem-sucedida

(2) ▶Exemplo: Plugin de Detecção de Ambiente (Uso Geral)

TYPESCRIPT
// plugins/env.ts
export default defineNuxtPlugin(() => {
  const isServer = import.meta.server
  const isClient = import.meta.client
  const isDev = import.meta.dev

  return {
    provide: {
      env: { isServer, isClient, isDev }
    }
  }
})

Saída:

TEXT
// Execução Bem-sucedida

(1) Comparação de Plugins do Lado do Servidor vs. Lado do Cliente

Dimensão .server.ts .client.ts .ts (Geral)
Execução SSR
Execução no Cliente
API do Navegador ❌ Não disponível ✅ Disponível ⚠️ Verificar primeiro
API Node.js ✅ Disponível ❌ Indisponível ⚠️ A definir
Dados Privados ✅ Seguro ❌ Pode ser exposto ⚠️ Cuidado

6. Ordem de Execução dos Plugins

(1) Regras de Ordenação

Regra Exemplo Ordem de Execução
Ordenado alfabeticamente pelo nome do arquivo 01-aaa.ts → 02-bbb.ts 01 é executado primeiro
Controle por Prefixo Numérico 01-env.ts → 02-db.ts → 03-api.ts Em ordem numérica
server antes de client db.server.ts → stripe.client.ts servidor primeiro durante SSR
plugins do nuxt.config por último plugins injetados via módulos plugins do framework são executados primeiro

(1) ▶Exemplo: Controlando a Ordem de Execução dos Plugins

TEXT
plugins/
├── 01-runtime-env.ts       # Primeiro: configurar ambiente
├── 02-database.server.ts   # Segundo: conectar banco de dados
├── 03-logger.ts            # Terceiro: inicializar logger
├── 04-stripe.client.ts     # Quarto: inicializar Stripe
└── 05-analytics.client.ts  # Quinto: inicializar analytics

Saída:

TEXT
Execução Bem-sucedida

(2) ▶Exemplo: Usando Outros Plugins Dentro de um Plugin

TYPESCRIPT
// plugins/05-analytics.client.ts
export default defineNuxtPlugin((nuxtApp) => {
  // Acessar plugin registrado anteriormente
  const { $logger } = nuxtApp

  $logger.info('Analytics plugin initialized')

  const analytics = {
    track(event: string, data?: any) {
      $logger.info(`Track: ${event}`, data)
      // Enviar para serviço de analytics
    },
    pageView(path: string) {
      $logger.info(`Page view: ${path}`)
    }
  }

  // Rastrear visualizações de página automaticamente
  nuxtApp.hook('page:finish', () => {
    analytics.pageView(window.location.pathname)
  })

  return { provide: { analytics } }
})

Saída:

TEXT
// Execução Bem-sucedida

7. Exemplo Completo: O Sistema de Plugins do MegaShop

TYPESCRIPT
// plugins/01-config.ts - Auxiliar de configuração de runtime
export default defineNuxtPlugin(() => {
  const config = useRuntimeConfig()
  return {
    provide: {
      config: {
        apiBase: config.public.apiBase,
        currency: 'USD',
        locale: 'en-US',
        maxCartItems: 99
      }
    }
  }
})
TYPESCRIPT
// plugins/02-logger.ts - Logging estruturado
export default defineNuxtPlugin(() => {
  const logger = {
    info(msg: string, ctx?: Record<string, any>) {
      console.log(JSON.stringify({ level: 'info', msg, ctx, ts: Date.now() }))
    },
    error(msg: string, ctx?: Record<string, any>) {
      console.error(JSON.stringify({ level: 'error', msg, ctx, ts: Date.now() }))
    }
  }
  return { provide: { logger } }
})
TYPESCRIPT
// plugins/03-stripe.client.ts - SDK de pagamento
export default defineNuxtPlugin(() => {
  const config = useRuntimeConfig()
  const stripe = window.Stripe(config.public.stripePublishableKey)
  return { provide: { stripe } }
})
TYPESCRIPT
// plugins/04-notification.client.ts - Notificações do navegador
export default defineNuxtPlugin(() => {
  const notify = {
    send(title: string, body: string) {
      if ('Notification' in window && Notification.permission === 'granted') {
        new Notification(title, { body })
      }
    }
  }
  return { provide: { notify } }
})

❓Perguntas Frequentes

P Qual é a diferença entre um plugin e um Composable?
R Um plugin é executado automaticamente uma vez quando o app inicia e é usado para inicializar bibliotecas de terceiros. Um Composable é chamado sob demanda dentro de um componente e é usado para reutilizar lógica. O Stripe usa um plugin para inicialização e um Composable para formatação de preço.
P Por que os nomes dos serviços injetados têm o prefixo $?
R O Nuxt recomenda prefixar os serviços injetados com $ ($stripe/$logger) para evitar conflitos com variáveis dentro dos componentes. Esta é uma convenção de nomenclatura do Nuxt, não um requisito.
P Posso usar a Pinia Store em um plugin?
R Sim, mas você precisa estar atento ao momento. O Pinia é inicializado antes do plugin, então você pode usar useXxxStore() dentro do plugin. No entanto, é recomendável gerenciar a Store dentro de um Composable e usar o plugin apenas para inicialização.
P Quando os plugins do lado do cliente são executados?
R Eles são executados durante a hidratação do lado do cliente. Plugins .client.ts não são executados durante a fase de SSR. Quando uma página é carregada pela primeira vez, os plugins do lado do cliente são executados após a conclusão da hidratação.
P Posso registrar plugins dinamicamente?
R Não recomendado. Os plugins do Nuxt são resolvidos em tempo de build e executados automaticamente em tempo de execução. Se precisar adiar a inicialização, use carregamento lazy dentro do plugin (como imports dinâmicos).
P O que devo fazer se um SDK de terceiros (como o Stripe) carregar lentamente?
R Carregue o SDK dinamicamente no plugin .client.ts para evitar bloquear a hidratação: const Stripe = (await import('@stripe/stripe-js')).default. Ou use a tag <script> para pré-carregá-la na seção <head>.

📖Resumo


📝Exercícios

  1. Exercício Básico (Dificuldade: ⭐): Crie um plugin de logger e use useNuxtApp().$logger.info() para registrar mensagens dentro de um componente.
  2. Exercício Avançado (Dificuldade: ⭐⭐): Crie um plugin Stripe no cliente e um plugin de banco de dados no servidor, e verifique que eles são executados em ambientes diferentes
  3. Desafio (Dificuldade: ⭐⭐⭐): Crie um plugin de analytics que use nuxtApp.hook('page:finish') para rastrear visualizações de página automaticamente e trabalhe com o plugin de logger para registrar dados.

---|

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%