O que Padrões Vite faz?
Padrões de build tool e servidor de desenvolvimento para projetos Vite 8+. Abrange configuração, variáveis de ambiente, proxy, modo biblioteca, pré-empacotamento de dependências e armadilhas comuns de produção.
Quando Usar
- Configurando
vite.config.tsouvite.config.js - Configurando variáveis de ambiente ou arquivos
.env - Configurando proxy do servidor de desenvolvimento para backends de API
- Otimizando a saída do build (chunks, minificação, assets)
- Publicando bibliotecas com
build.lib - Resolvendo problemas de pré-empacotamento de dependências ou interoperabilidade CJS/ESM
- Depurando HMR, servidor de desenvolvimento ou erros de build
- Escolhendo ou ordenando plugins Vite
Como Funciona
- Modo dev serve arquivos fonte como ESM nativo — sem empacotamento. As transformações acontecem sob demanda por requisição de módulo, por isso as inicializações a frio são rápidas e o HMR é preciso.
- Modo build usa Rolldown (v7+) ou Rollup (v5–v6) para empacotar o aplicativo para produção com tree-shaking, code-splitting e minificação baseada em Oxc.
- Pré-empacotamento de dependências converte dependências CJS/UMD para ESM uma vez via esbuild e armazena o resultado em cache em
node_modules/.vite, para que inicializações subsequentes ignorem esse trabalho. - Plugins compartilham uma interface unificada entre dev e build — o mesmo objeto de plugin funciona tanto para as transformações sob demanda do servidor de desenvolvimento quanto para o pipeline de produção.
- Variáveis de ambiente são inlineadas estaticamente no momento do build. Variáveis prefixadas com
VITE_tornam-se constantes públicas no bundle; tudo sem prefixo é invisível para o código cliente.
Exemplos
Estrutura de Configuração
Configuração Básica
// vite.config.ts
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
export default defineConfig({
plugins: [react()],
resolve: {
alias: { '@': new URL('./src', import.meta.url).pathname },
},
})
Configuração Condicional
// vite.config.ts
import { defineConfig, loadEnv } from 'vite'
import react from '@vitejs/plugin-react'
export default defineConfig(({ command, mode }) => {
const env = loadEnv(mode, process.cwd()) // Apenas prefixo VITE_ (seguro)
return {
plugins: [react()],
server: command === 'serve' ? { port: 3000 } : undefined,
define: {
__API_URL__: JSON.stringify(env.VITE_API_URL),
},
}
})
Opções Chave de Configuração
| Chave | Padrão | Descrição |
|---|---|---|
root | '.' | Raiz do projeto (onde index.html reside) |
base | '/' | Caminho base público para assets implantados |
envPrefix | 'VITE_' | Prefixo para variáveis de ambiente expostas ao cliente |
build.outDir | 'dist' | Diretório de saída |
build.minify | 'oxc' | Minificador ('oxc', 'terser' ou false) |
build.sourcemap | false | true, 'inline' ou 'hidden' |
Plugins
Plugins Essenciais
A maioria das necessidades de plugins é coberta por alguns pacotes bem mantidos. Recorra a estes antes de escrever o seu próprio.
| Plugin | Propósito | Quando usar |
|---|---|---|
@vitejs/plugin-react-swc | React HMR + Fast Refresh via SWC | Padrão para aplicativos React (mais rápido que a variante Babel) |
@vitejs/plugin-react | React HMR + Fast Refresh via Babel | Apenas se precisar de plugins Babel (emotion, decorators MobX) |
@vitejs/plugin-vue | Suporte a Vue 3 SFC | Aplicativos Vue |
vite-plugin-checker | Executa tsc + ESLint em thread de worker com overlay HMR | Qualquer aplicativo TypeScript — Vite NÃO faz verificação de tipos durante vite build |
vite-tsconfig-paths | Honra aliases paths do tsconfig.json | Qualquer momento em que você já tenha aliases em tsconfig.json |
vite-plugin-dts | Emite arquivos .d.ts no modo biblioteca | Publicando bibliotecas TypeScript |
vite-plugin-svgr | Importa SVGs como componentes React | Aplicativos React que usam SVGs como componentes |
rollup-plugin-visualizer | Relatório de treemap/sunburst do bundle | Auditorias periódicas de tamanho do bundle (use enforce: 'post') |
vite-plugin-pwa | PWA + Workbox sem configuração | Aplicativos com suporte offline |
Observação crítica: vite build transpila, mas NÃO faz verificação de tipos. Erros de tipo são enviados silenciosamente para produção a menos que você adicione vite-plugin-checker ou execute tsc --noEmit no CI.
Criando Plugins Personalizados
Criar plugins é raro — a maioria das necessidades é coberta por plugins existentes. Quando precisar de um, comece inline em vite.config.ts e extraia apenas se for reutilizado.
// vite.config.ts — plugin inline mínimo
function myPlugin(): Plugin {
return {
name: 'my-plugin', // obrigatório, deve ser único
enforce: 'pre', // 'pre' | 'post' (opcional)
apply: 'build', // 'build' | 'serve' (opcional)
transform(code, id) {
if (!id.endsWith('.custom')) return
return { code: transformCustom(code), map: null }
},
}
}
Hooks principais: transform (modificar fonte), resolveId + load (módulos virtuais), transformIndexHtml (injetar no HTML), configureServer (adicionar middleware de dev), hotUpdate (HMR personalizado — substitui o handleHotUpdate obsoleto no v7+).
Módulos virtuais usam a convenção de prefixo \0 — resolveId retorna '\0virtual:my-id' para que outros plugins o ignorem. O código do usuário importa 'virtual:my-id'.
Para a API completa de plugins, consulte vite.dev/guide/api-plugin. Use vite-plugin-inspect durante o desenvolvimento para depurar o pipeline de transformação.
API HMR
Plugins de framework (@vitejs/plugin-react, @vitejs/plugin-vue, etc.) lidam com HMR automaticamente. Recorra a import.meta.hot diretamente apenas ao construir stores de estado personalizados, ferramentas de desenvolvimento ou utilitários agnósticos de framework que precisam persistir estado entre atualizações.
// src/store.ts — HMR manual para um módulo vanilla
if (import.meta.hot) {
// Persistir estado entre atualizações (deve MUTAR, nunca reatribuir .data)
import.meta.hot.data.count = import.meta.hot.data.count ?? 0
// Limpar efeitos colaterais antes de o módulo ser substituído
import.meta.hot.dispose((data) => clearInterval(data.intervalId))
// Aceitar atualizações deste módulo
import.meta.hot.accept()
}
Todo código import.meta.hot é removido por tree-shaking nos builds de produção — nenhuma remoção de guarda é necessária.
Variáveis de Ambiente
Vite carrega .env, .env.local, .env.[mode] e .env.[mode].local nesta ordem (as posteriores substituem as anteriores); arquivos *.local são ignorados pelo git e destinam-se a segredos locais.
Acesso do Lado do Cliente
Apenas variáveis prefixadas com VITE_ são expostas ao código cliente:
import.meta.env.VITE_API_URL // string
import.meta.env.MODE // 'development' | 'production' | custom
import.meta.env.BASE_URL // valor da configuração base
import.meta.env.DEV // boolean
import.meta.env.PROD // boolean
import.meta.env.SSR // boolean
Usando Env na Configuração
// vite.config.ts
import { defineConfig, loadEnv } from 'vite'
export default defineConfig(({ mode }) => {
const env = loadEnv(mode, process.cwd()) // Apenas prefixo VITE_ (seguro)
return {
define: {
__API_URL__: JSON.stringify(env.VITE_API_URL),
},
}
})
Segurança
Prefixo VITE_ NÃO é um Limite de Segurança
Qualquer variável prefixada com VITE_ é inlineada estaticamente no bundle do cliente no momento do build. Minificação, codificação base64 e desabilitação de source maps NÃO a ocultam. Um atacante determinado pode extrair qualquer variável VITE_ do JavaScript enviado.
Regra: Apenas valores públicos (URLs de API, flags de funcionalidade, chaves públicas) vão em variáveis VITE_. Segredos (tokens de API, URLs de banco de dados, chaves privadas) DEVEM viver no lado do servidor, por trás de uma API ou função serverless.
A Armadilha de loadEnv('')
// RUIM: passar '' como terceiro argumento carrega TODAS as variáveis de ambiente — incluindo segredos do servidor —
// e as torna disponíveis para serem inlineadas no código cliente via `define`.
const env = loadEnv(mode, process.cwd(), '')
// BOM: lista explícita de prefixos
const env = loadEnv(mode, process.cwd(), ['VITE_', 'APP_'])
Source Maps em Produção
Source maps de produção vazam seu código fonte original. Desabilite-os a menos que os envie para um rastreador de erros (Sentry, Bugsnag) e os exclua localmente depois:
build: {
sourcemap: false, // padrão — mantenha assim
}
Checklist .gitignore
.env.local,.env.*.local— substituições de segredos locaisdist/— saída do buildnode_modules/.vite— cache de pré-empacotamento (entradas obsoletas causam erros fantasmas)
Proxy do Servidor
// vite.config.ts — server.proxy
server: {
proxy: {
'/foo': 'http://localhost:4567', // atalho de string
'/api': {
target: 'http://localhost:8080',
changeOrigin: true, // necessário para backends com host virtual
rewrite: (path) => path.replace(/^\/api/, ''),
},
},
}
Para proxy WebSocket, adicione ws: true à configuração da rota.
Otimização de Build
Chunks Manuais
// vite.config.ts — build.rolldownOptions
build: {
rolldownOptions: {
output: {
// Forma de objeto: agrupar pacotes específicos
manualChunks: {
'react-vendor': ['react', 'react-dom'],
'ui-vendor': ['@radix-ui/react-dialog', '@radix-ui/react-popover'],
},
},
},
}
// Forma de função: dividir por heurística
manualChunks(id) {
if (id.includes('node_modules/react')) return 'react-vendor'
if (id.includes('node_modules')) return 'vendor'
}
Desempenho
Evite Arquivos Barrel
Arquivos barrel (index.ts reexportando tudo de um diretório) forçam o Vite a carregar cada arquivo reexportado mesmo quando você importa um único símbolo. Esta é a causa #1 de lentidão no servidor de desenvolvimento sinalizada pela documentação oficial.
// RUIM — importar um util força o Vite a carregar todo o barrel
import { slash } from '@/utils'
// BOM — importação direta, apenas o arquivo é carregado
import { slash } from '@/utils/slash'
Seja Explícito com Extensões de Importação
Cada extensão implícita força até 6 verificações no sistema de arquivos via resolve.extensions. Em bases de código grandes, isso se acumula.
// RUIM
import Component from './Component'
// BOM
import Component from './Component.tsx'
Restrinja tsconfig.json allowImportingTsExtensions + resolve.extensions apenas às extensões que você realmente usa.
Aquecimento de Rotas de Caminho Quente
server.warmup.clientFiles pré-transforma entradas quentes conhecidas antes de o navegador solicitá-las — eliminando a cascata de requisições de carregamento a frio em aplicativos grandes.
// vite.config.ts
server: {
warmup: {
clientFiles: ['./src/main.tsx', './src/routes/**/*.tsx'],
},
}
Perfilando Servidores de Desenvolvimento Lentos
Quando vite dev parece lento, comece com vite --profile, interaja com o aplicativo, depois pressione p+enter para salvar um .cpuprofile. Carregue-o no Speedscope para descobrir quais plugins estão consumindo tempo — geralmente hooks buildStart, config ou configResolved em plugins da comunidade.
Modo Biblioteca
Ao publicar um pacote npm, use build.lib. Duas armadilhas importam mais do que detalhes de configuração:
- Tipos não são emitidos — adicione
vite-plugin-dtsou executetsc --emitDeclarationOnlyseparadamente. - Dependências pares DEVEM ser externalizadas — pares não listados são empacotados em sua biblioteca, causando erros de runtime duplicado em consumidores.
// vite.config.ts
build: {
lib: {
entry: 'src/index.ts',
formats: ['es', 'cjs'],
fileName: (format) => `my-lib.${format}.js`,
},
rolldownOptions: {
external: ['react', 'react-dom', 'react/jsx-runtime'], // toda dependência par
},
}
Externals SSR
Configurações simples de createServer({ middlewareMode: true }) são território de autores de framework. A maioria dos aplicativos deve usar Nuxt, Remix, SvelteKit, Astro ou TanStack Start. O que você ajustará como usuário de framework é a configuração de externals quando dependências quebram em SSR:
// vite.config.ts — opções ssr
ssr: {
external: ['node-native-package'], // manter como require() no bundle SSR
noExternal: ['esm-only-package'], // forçar empacotamento na saída SSR (corrige a maioria dos erros SSR)
target: 'node', // 'node' ou 'webworker'
}
Pré-empacotamento de Dependências
Vite pré-empacota dependências para converter CJS/UMD para ESM e reduzir o número de requisições.
// vite.config.ts — optimizeDeps
optimizeDeps: {
include: [
'lodash-es', // forçar pré-empacotamento de dependências pesadas conhecidas
'cjs-package', // dependências CJS que causam problemas de interop
'deep-lib/components/**', // glob para imports profundos
],
exclude: ['local-esm-package'], // deve ser ESM válido se excluído
force: true, // ignorar cache, re-otimizar (depuração temporária)
}
Armadilhas Comuns
Dev Não Corresponde ao Build
Dev usa esbuild/Rolldown para transformações; build usa Rolldown para empacotamento. Bibliotecas CJS podem se comportar de forma diferente entre os dois. Sempre verifique com vite build && vite preview antes de implantar.
Chunks Obsoletos Após Implantação
Novos builds produzem novos hashes de chunks. Usuários com sessões ativas solicitam nomes de arquivos antigos que não existem mais. Vite não tem solução embutida. Mitigações:
- Mantenha arquivos antigos de
dist/assets/vivos por uma janela de implantação - Capture erros de importação dinâmica em seu roteador e force um recarregamento de página
Docker e Contêineres
Vite vincula a localhost por padrão, que é inacessível de fora de um contêiner:
// vite.config.ts — configuração Docker/contêiner
server: {
host: true, // vincular 0.0.0.0
hmr: { clientPort: 3000 }, // se atrás de um proxy reverso
}
Acesso a Arquivos em Monorepo
Vite restringe o serviço de arquivos à raiz do projeto. Pacotes fora da raiz são bloqueados:
// vite.config.ts — acesso a arquivos em monorepo
server: {
fs: {
allow: ['..'], // permitir diretório pai (raiz do workspace)
},
}
Anti-Padrões
// RUIM: Definir envPrefix como '' expõe TODAS as variáveis de ambiente (incluindo segredos) ao cliente
envPrefix: ''
// RUIM: Assumir que require() funciona em código do cliente
// RUIM: Ignorar erros de HMR no console
Resolução de Problemas
HMR Não Funciona
- Verifique se o plugin do framework está instalado e incluído
- Certifique-se de que a exportação nomeada do componente corresponda ao nome do arquivo
- Verifique se não há erros de sintaxe no console do navegador
- Tente
vite --forcepara limpar o cache de pré-empacotamento
Erros de Build
# Erro: "Build failed with 1 error"
# Verifique a mensagem de erro completa — geralmente é um erro de sintaxe ou tipo
# Erro: "Could not resolve"
# Verifique se o caminho de importação está correto e se o pacote está instalado
# Erro: "Out of memory"
# Aumente a memória do Node.js: NODE_OPTIONS="--max-old-space-size=4096" vite build
Ferramentas de Diagnóstico
vite --debug— modo de depuração detalhadavite-plugin-inspect— inspecionar pipeline de transformação (http://localhost:5173/__inspect)debuggerno código fonte — funciona com HMRconsole.logcom source maps ativados