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
- Quando o diretório "plugins/" é automaticamente registrado e executado
- defineNuxtPlugin: Usando
provideeinject - Plugins do lado do servidor/cliente: Distinguidos pelas extensões de arquivo .server.ts e .client.ts
- Controle da Ordem de Execução dos Plugins
- Guia Prático do SDK de Pagamento e Plugin de Logging do MegaShop
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:
// 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
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
// 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:
// 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)
// plugins/stripe.client.ts
export default defineNuxtPlugin(() => {
const config = useRuntimeConfig()
const stripe = window.Stripe(config.public.stripePublishableKey)
return {
provide: {
stripe
}
}
})
Saída:
// Execução Bem-sucedida
(2) ▶Exemplo: Usando um plugin em um componente
<!-- 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:
// Execução Bem-sucedida
(3) ▶Exemplo: Plugin de Banco de Dados do Lado do Servidor
// 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:
// 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)
// 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:
// Execução Bem-sucedida
(2) ▶Exemplo: Plugin de Detecção de Ambiente (Uso Geral)
// 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:
// 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
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:
Execução Bem-sucedida
(2) ▶Exemplo: Usando Outros Plugins Dentro de um Plugin
// 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:
// Execução Bem-sucedida
7. Exemplo Completo: O Sistema de Plugins do MegaShop
// 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
}
}
}
})
// 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 } }
})
// plugins/03-stripe.client.ts - SDK de pagamento
export default defineNuxtPlugin(() => {
const config = useRuntimeConfig()
const stripe = window.Stripe(config.public.stripePublishableKey)
return { provide: { stripe } }
})
// 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
useXxxStore() dentro do plugin. No entanto, é recomendável gerenciar a Store dentro de um Composable e usar o plugin apenas para inicialização.const Stripe = (await import('@stripe/stripe-js')).default. Ou use a tag <script> para pré-carregá-la na seção <head>.📖Resumo
- O diretório
plugins/é registrado automaticamente, e os arquivos são executados em ordem alfabética pelo nome do arquivo defineNuxtPlugin+provideinjeta serviços globais; componentes os acessam viauseNuxtApp().$xxx- As extensões de arquivo .server.ts e .client.ts distinguem os ambientes de execução para evitar conflitos entre APIs do navegador e Node
- Prefixos numéricos controlam a ordem de execução e garantem que as dependências sejam tratadas corretamente
- MegaShop integra serviços de terceiros como pagamentos Stripe, Logger e Notificações via plugins
📝Exercícios
- Exercício Básico (Dificuldade: ⭐): Crie um plugin de logger e use
useNuxtApp().$logger.info()para registrar mensagens dentro de um componente. - 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
- 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.
---|



