🎯 Vite — Complete Cheatsheet 🎯
Vite es una herramienta de construcción frontend de última generación que combina un servidor de desarrollo ultrarrápido basado en módulos nativos del navegador con un empaquetador de producción optimizado mediante Rollup. Elimina la espera de rebundling completo durante el desarrollo, entrega HMR de latencia sub-100ms y mantiene bundles de producción altamente optimizados. Este cheatsheet cubre desde la instalación y configuración esencial hasta el sistema de plugins, manejo de entornos, SSR, modo librería, optimización avanzada y patrones de producción. Ideal para desarrolladores que buscan reemplazar Webpack, migrar a ESM nativo o crear nuevos proyectos con tiempos de arranque instantáneos y flujos de trabajo modernos.
1. 🌟 Conceptos Fundamentales
- Módulos Nativos ESM: Vite sirve archivos directamente como
<script type="module">. El navegador resuelve imports bajo demanda, sin paso de compilación en dev.- Por qué importa: Arranque instantáneo independiente del tamaño del proyecto.
- Pre-bundling de Dependencias: Usa
esbuildpara convertir dependencias CJS/UMD a ESM y unificar múltiples módulos en un solo archivo caché.- Reduce drásticamente la cantidad de peticiones HTTP y evita parseo de CommonJS en el navegador.
- HMR (Hot Module Replacement): Reemplaza solo el módulo modificado y sus dependientes sin recargar la página.
- Funciona vía WebSockets. Mantiene el estado de la aplicación y el contexto de ejecución.
- Rollup para Producción: El build de producción delega en Rollup para tree-shaking avanzado, code splitting y optimización de assets.
- Dev y prod usan pipelines diferentes por diseño: velocidad vs optimización.
- Configuración Declarativa:
vite.config.js(o.ts) centraliza servidor, build, plugins y resolución. Soporta funciones asíncronas y entornos dinámicos. - Aislamiento de Entornos: Variables de cliente y servidor se separan explícitamente. Solo las prefijadas con
VITE_se exponen al frontend. - Plugin API Unificada: Hereda la arquitectura de Rollup pero añade hooks específicos para dev (
handleHotUpdate,configureServer). - Framework Agnostic: Base neutra que se adapta a React, Vue, Svelte, Solid, Preact o vanilla JS mediante plugins oficiales.
2.
Instalación y Estructura
- Creación rápida con scaffolding:
npm create vite@latest my-app -- --template react # Opciones: vanilla, vue, svelte, preact, solid, lit, qwik, react-ts, etc. cd my-app npm install - Instalación manual en proyecto existente:
npm install -D vite # package.json scripts: # "dev": "vite", "build": "vite build", "preview": "vite preview" - Estructura de proyecto estándar:
my-app/ ├── index.html # Entry point real (NO está en src/) ├── vite.config.ts # Configuración principal ├── package.json ├── src/ │ ├── main.js # Punto de entrada JS/TS │ ├── App.jsx # Componente raíz │ └── assets/ # Imágenes, fuentes, estilos ├── public/ # Assets estáticos (copiados sin transformación) └── dist/ # Output de build (generado) index.htmlcomo módulo raíz:<!DOCTYPE html> <html lang="es"> <head> <meta charset="UTF-8"> <title>Mi App Vite</title> </head> <body> <div id="app"></div> <!-- Vite intercepta esta ruta y sirve main.js como ESM --> <script type="module" src="/src/main.js"></script> </body> </html>
3. 📝 CLI y Comandos Esenciales
| Comando | Descripción | Flags comunes |
|---|---|---|
vite | Inicia servidor de desarrollo | --host, --port, --open, --strictPort |
vite build | Compila para producción | --outDir, --emptyOutDir, --sourcemap, --minify |
vite preview | Sirve dist/ localmente | --port, --strictPort, --host |
vite optimize | Fuerza re-bundling de dependencias | --force |
- Flags de servidor útiles:
vite --host 0.0.0.0 --port 3000 --open --strictPort # Exponer en red, puerto fijo, abrir navegador, rechazar si puerto ocupado - Modos y per
s:
vite build --mode staging # Carga .env.staging vite --mode production # Fuerza producción en dev (útil para debug) - Limpieza de caché:
rm -rf node_modules/.vite # Forzar recompilación de dependencias vite optimize --force # Reconstruir pre-bundle manualmente
4. ⚙️ Configuración (vite.config.js)
- Estructura básica con TypeScript:
import { defineConfig } from 'vite' import react from '@vitejs/plugin-react' export default defineConfig({ plugins: [react()], root: '.', // Directorio base (default: '.') base: '/my-app/', // Ruta pública base (CDN o subpath) publicDir: 'public', // Assets estáticos clearScreen: true, // Limpiar terminal al reiniciar logLevel: 'info', // 'info' | 'warn' | 'error' | 'silent' }) - Configuración del servidor (
server):server: { port: 5173, strictPort: true, host: true, // '0.0.0.0' o 'localhost' open: '/dashboard', // Ruta inicial al abrir navegador proxy: { '/api': { target: 'https://backend.dev', changeOrigin: true, secure: false, rewrite: (path) => path.replace(/^\/api/, ''), }, }, cors: true, fs: { strict: true }, // Evitar acceso fuera de raíz del proyecto watch: { ignored: ['**/node_modules/**', '**/.git/**'] }, } - Configuración de build (
build):build: { target: 'es2020', // Target de transpilación outDir: 'dist', assetsDir: 'assets', sourcemap: false, // true | 'inline' | 'hidden' minify: 'esbuild', // 'esbuild' | 'terser' | false cssCodeSplit: true, // Dividir CSS por chunk rollupOptions: { output: { manualChunks(id) { if (id.includes('node_modules/lodash')) return 'vendor-lodash' if (id.includes('node_modules/moment')) return 'vendor-moment' }, chunkFileNames: 'js/[name]-[hash].js', assetFileNames: 'assets/[name]-[hash][extname]', }, }, } - Resolución y CSS (
resolve/css):resolve: { alias: { '@': path.resolve(__dirname, 'src'), '@utils': path.resolve(__dirname, 'src/utils'), }, extensions: ['.mjs', '.js', '.ts', '.jsx', '.tsx', '.json'], }, css: { preprocessorOptions: { scss: { additionalData: '@import "src/styles/variables.scss";' }, }, modules: { localsConvention: 'camelCase' }, devSourcemap: true, },
5. 🔌 Sistema de Plugins
- Plugins oficiales:
@vitejs/plugin-react,@vitejs/plugin-vue,@vitejs/plugin-legacy,@vitejs/plugin-basic-ssl. - Comunidad popular:
vite-plugin-pwa,vite-plugin-svg-icons,unplugin-vue-components,vite-plugin-inspect. - Hook de configuración (
config):export default { name: 'vite-plugin-env-prefix', config(config) { return { define: { __APP_VERSION__: JSON.stringify('1.0.0') } } }, } - Transformación de archivos (
transform):transform(code, id) { if (!id.endsWith('.md')) return // Convertir Markdown a componente JS const html = markdownToHtml(code) return `export default function Markdown() { return \`${html}\` }` } - Interceptar módulos no existentes (
load):load(id) { if (id === 'virtual:config') { return `export const config = { theme: 'dark', lang: 'es' }` } } - Orden de ejecución: Plugins se ejecutan en el orden definido. Los hooks
configse resuelven en orden inverso para permitir sobrescritura.enforce: 'pre'oenforce: 'post'controla prioridad explícita. - Dev-only vs Prod-only:
// Solo en dev configureServer(server) { /* middleware express/connect */ } // Solo en build generateBundle(options, bundle) { /* manipular archivos finales */ }
6. 🌍 Variables de Entorno y Modo
-
Archivos
.envsoportados:Archivo Modo Uso .envTodos Valores compartidos .env.localTodos Ignorado por git, overrides locales .env.productionproductionSolo build/prod .env.developmentdevelopmentSolo dev .env.stagingstaging--mode staging -
Prefijo de seguridad: Solo variables con
VITE_se exponen al cliente.# .env VITE_API_URL=https://api.prod.com DATABASE_SECRET=supersecret # ❌ No llega al frontend VITE_FEATURE_FLAG=true -
Acceso en código:
const apiUrl = import.meta.env.VITE_API_URL const isProd = import.meta.env.PROD // true si mode === 'production' const isDev = import.meta.env.DEV // true si mode === 'development' const mode = import.meta.env.MODE // string del modo activo -
Inyección en HTML:
<!-- Disponible en index.html --> <title>%VITE_APP_NAME%</title> <meta name="description" content="%VITE_APP_DESC%"> -
Definición compile-time (
define):define: { __BUILD_TIME__: JSON.stringify(new Date().toISOString()), __DEBUG__: process.env.NODE_ENV !== 'production', } // Se reemplaza estáticamente en el bundle (no es runtime)
7. 📦 SSR y Modo Librería
- Build de librería (
build.lib):build: { lib: { entry: path.resolve(__dirname, 'src/index.ts'), name: 'MyLib', formats: ['es', 'umd'],
Name: (format) => `my-lib.${format}.js`,
},
rollupOptions: {
external: ['react', 'react-dom'],
output: { globals: { react: 'React', 'react-dom': 'ReactDOM' } },
},
} - Renderizado del lado del servidor (SSR):
vite build --ssr src/entry-server.ts --outDir dist-serverbuild: { ssr: true, rollupOptions: { input: 'src/entry-server.ts' }, } - Hidratación básica:
// entry-server.ts import { renderToString } from 'react-dom/server' export function render(url) { const html = renderToString(<App />) return `<div id="app">${html}</div>` } - Externals en SSR:
ssr.noExternal: ['my-local-pkg']fuerza incluir dependencias en el bundle del servidor. Default: externaliza todonode_modules. - Dynamic Imports para SSR:
const { default: component } = await import(`./pages/${route}.tsx`) // Vite resuelve rutas dinámicas estáticamente durante build
8. ⚡ Optimización y Rendimiento
- Pre-bundling optimizado (
optimizeDeps):optimizeDeps: { include: ['lodash/debounce', 'chart.js'], exclude: ['my-local-package'], esbuildOptions: { target: 'es2022', jsx: 'automatic' }, } - Code Splitting manual:
build: { rollupOptions: { output: { manualChunks(id) { if (id.includes('node_modules')) { return 'vendor' } if (id.includes('/pages/')) { const page = id.split('/pages/')[1].split('.')[0] return `page-${page}` } }, }, }, } - Precalentamiento de rutas (
server.warmup):server: { warmup: { clientFiles: ['./src/components/**/*', './src/styles/main.css'], ssrFiles: ['./src/server/api.ts'], }, } // Reduce latencia de primer request al transformar módulos críticos - Caché de transformaciones: Vite cachea resultados en
node_modules/.vite. Limpiar si hay corrupción o cambios drásticos de configuración. - Assets grandes y
limit:build: { assetsInlineLimit: 4096, // >= 4KB se convierte en URL, < se inlines como base64 } - Tree-shaking en dev: Vite no hace tree-shaking en dev por diseño. Para validar eliminación de código, usar
vite build --watchorollup-plugin-visualizer.
9. ⚠️ Errores Comunes y Trampas
- Filtrar variables sin prefijo
VITE_:API_KEYno llega al frontend.- Fix: Renombrar a
VITE_API_KEYo usardefinepara compile-time.
- Fix: Renombrar a
- HMR no actualiza archivos no importados: Vite solo rastrea el grafo de módulos ESM. Archivos en
public/o inyectados dinámicamente sinimportno disparan HMR.- Fix: Importar explícitamente o usar
import.meta.hot.accept()manual.
- Fix: Importar explícitamente o usar
- Confusión entre
baseypublicDir:basecambia rutas en build.publicDircopia archivos tal cual adist/. Usarbasepara subpaths, no para assets estáticos relativos.- Fix: Referenciar assets públicos con
/img/logo.png(ruta absoluta), no./img/logo.png.
- Fix: Referenciar assets públicos con
- Plugins en orden incorrecto: Un plugin que transforma
.vuedebe ejecutarse antes de uno que analiza JSX.- Fix: Usar
enforce: 'pre'/enforce: 'post'y verificar@rollup/pluginutilsorder.
- Fix: Usar
- Dependencias CJS rotas en dev: Algunos paquetes usan
require()en archivos no empaquetados.- Fix: Agregar a
optimizeDeps.includeo reportar issue al autor para migrar a ESM.
- Fix: Agregar a
- SSR Hydration Mismatch: HTML generado en servidor difiere del cliente (fechas, random,
windowchecks).- Fix: Envolver lógica no-determinista en
useEffectoonMounted. Validartypeof window !== 'undefined'.
- Fix: Envolver lógica no-determinista en
- Dev-only code en producción:
console.logodebuggerno se eliminan automáticamente si no usanimport.meta.env.DEV.- Fix: Envolver con
if (import.meta.env.DEV) { ... }para tree-shaking seguro.
- Fix: Envolver con
10.
Mejores Prácticas y Consejos de Experto
- Usa
defineConfigcon TypeScript: Habilita autocompletado, validación de tipos y detección temprana de configuraciones inválidas. - Prefiere ESM nativo en dependencias: Evita
cjssiempre que sea posible. Si es inevitable, usaoptimizeDepsy reporta a los mantenedores. - Configura
server.watchpara monorepos: Si usas workspaces, añadeignored: ['!**/node_modules/my-internal-lib/**']para que Vite observe cambios en paquetes locales. - Valida el build de producción temprano: Ejecuta
vite builden CI antes de mergear. Dev y prod son pipelines distintos; errores de tree-shaking o externalización solo aparecen en build. - Usa
vite-plugin-inspectpara debug: Visualiza el grafo de módulos, transformaciones y tiempos de compilación en tiempo real. Invaluable para optimizar plugins lentos. - Sustituye Webpack aliases con
resolve.alias: Mantén rutas cortas y consistentes. Usa@parasrc/y evitas../../../hell. - Integra Vitest nativamente: Compartir configuración de Vite con Vitest garantiza consistencia en resolución, alias y plugins entre app y tests.
- Evita transforms pesados en dev: Plugins que ejecutan compiladores completos (ej.
sass,stylus,swccustom) ralentizan HMR. Cachea resultados o usa alternativas nativas cuando sea posible. - Perfila con
rollup-plugin-visualizer: Genera treemap del bundle final. Identifica dependencias ocultas, duplicados o chunks innecesarios antes de deploy. - Documenta
VITE_env vars en.env.example: Centraliza la configuración requerida. Usazodovalibotenmain.tspara validar variables al inicio de la app y fallar rápido si faltan.
Este cheatsheet proporciona una referencia completa para Vite, cubriendo arquitectura ESM nativa, configuración avanzada, sistema de plugins, manejo de entornos, SSR y modo librería, optimización de builds y patrones de producción, junto con las mejores prácticas para mantener flujos de trabajo rápidos, seguros y escalables en entornos frontend modernos.