Configuración de build en Vite
Vite genera un build optimizado para producción con minificación, tree-shaking y code splitting automático. La configuración del build se define en vite.config.ts dentro de la propiedad build:
// vite.config.ts
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
export default defineConfig({
plugins: [vue()],
build: {
outDir: 'dist', // Directorio de salida
minify: 'esbuild', // Minificador: 'esbuild' (rápido) o 'terser' (más compresión)
sourcemap: false, // Generar sourcemaps (true, false, 'inline', 'hidden')
target: 'es2020', // Target de compilación
cssCodeSplit: true, // Separar CSS por chunk
assetsInlineLimit: 4096, // Inlinear assets menores a 4KB como base64
},
})
Opciones de build relevantes
outDir: directorio donde se genera el build. Por defecto esdistminify:'esbuild'es más rápido,'terser'produce bundles ligeramente más pequeñossourcemap:truegenera sourcemaps públicos,'hidden'los genera pero no los referencia desde los archivos JS (útil para error tracking sin exponer el código fuente)target: define el target de JavaScript para la transpilación.'es2020'es adecuado para navegadores modernoscssCodeSplit: cuando estrue, el CSS de cada chunk se extrae en un archivo separado
Ejecutar el build en local
Desde la raíz del proyecto, el comando habitual es:
npm run build

Los archivos listos para producción se generan en el directorio dist/:

Variables de entorno
Vite utiliza archivos .env para gestionar variables de entorno. Los archivos se cargan según el modo de ejecución:
.env # Cargado siempre
.env.local # Cargado siempre, ignorado por git
.env.development # Cargado en modo development
.env.development.local # Cargado en modo development, ignorado por git
.env.production # Cargado en modo production
.env.production.local # Cargado en modo production, ignorado por git
Prefijo VITE_ para variables del cliente
Solo las variables con el prefijo VITE_ se exponen al código del cliente. Las variables sin este prefijo solo están disponibles en el entorno de build (Node.js):
# .env
VITE_API_URL=https://api.example.com
VITE_APP_TITLE=Mi aplicación
SECRET_KEY=esto-no-se-expone-al-cliente
Acceso a variables de entorno
En el código de la aplicación, las variables se acceden con import.meta.env:
<script setup lang="ts">
const apiUrl = import.meta.env.VITE_API_URL
const appTitle = import.meta.env.VITE_APP_TITLE
const mode = import.meta.env.MODE // 'development' o 'production'
const isDev = import.meta.env.DEV // true en desarrollo
const isProd = import.meta.env.PROD // true en producción
const baseUrl = import.meta.env.BASE_URL // base URL configurada
</script>
<template>
<header>
<h1>{{ appTitle }}</h1>
<span v-if="isDev" class="badge-dev">DEV</span>
</header>
</template>
Tipado de variables de entorno
Para obtener autocompletado en TypeScript, se crea un archivo de declaración:
// env.d.ts
/// <reference types="vite/client" />
interface ImportMetaEnv {
readonly VITE_API_URL: string
readonly VITE_APP_TITLE: string
}
interface ImportMeta {
readonly env: ImportMetaEnv
}
Modos personalizados
Además de development y production, se pueden crear modos personalizados con archivos .env específicos:
# .env.staging
VITE_API_URL=https://staging-api.example.com
VITE_APP_TITLE=Mi App (Staging)
# Build con modo staging
npx vite build --mode staging
Estrategias de chunk splitting
Vite divide automáticamente el código en chunks, pero se puede personalizar la estrategia para optimizar la caché del navegador:
// vite.config.ts
export default defineConfig({
build: {
rollupOptions: {
output: {
manualChunks: {
// Agrupar librerías de Vue en un chunk separado
'vue-vendor': ['vue', 'vue-router', 'pinia'],
// Agrupar librerías UI
'ui-vendor': ['@headlessui/vue', '@heroicons/vue'],
},
},
},
},
})
Una estrategia alternativa es dividir por patrón de ruta de módulo:
export default defineConfig({
build: {
rollupOptions: {
output: {
manualChunks(id) {
if (id.includes('node_modules')) {
// Crear un chunk por cada paquete principal
const packageName = id.split('node_modules/')[1].split('/')[0]
if (['vue', 'vue-router', 'pinia'].includes(packageName)) {
return 'vue-vendor'
}
return 'vendor'
}
},
},
},
},
})
El beneficio de separar vendor chunks es que las librerías externas cambian con poca frecuencia, por lo que el navegador puede mantenerlas en caché incluso cuando se actualiza el código de la aplicación.
Static asset handling
Directorio public
Los archivos en el directorio public/ se sirven tal cual en la raíz del sitio, sin procesamiento. Es adecuado para favicon, robots.txt, archivos de configuración y assets que necesitan mantener su nombre exacto:
public/
favicon.ico
robots.txt
og-image.png
Se referencian con rutas absolutas desde la raíz:
<template>
<img src="/og-image.png" alt="Open Graph" />
</template>
Importación de assets
Los assets importados directamente en el código se procesan por Vite, reciben un hash en el nombre y se optimizan:
<script setup lang="ts">
import logoUrl from '@/assets/logo.png'
</script>
<template>
<img :src="logoUrl" alt="Logo" />
</template>
Para generar URLs de forma dinámica:
function getImageUrl(name: string): string {
return new URL(`../assets/images/${name}`, import.meta.url).href
}
Despliegue en plataformas estáticas
Una aplicación Vue generada con npm run build produce archivos estáticos en dist/ que se pueden servir desde cualquier servidor de archivos estáticos.
Netlify
Crear un archivo netlify.toml en la raíz del proyecto:
[build]
command = "npm run build"
publish = "dist"
[[redirects]]
from = "/*"
to = "/index.html"
status = 200
La regla de redirect es necesaria para que el enrutamiento HTML5 History de Vue Router funcione correctamente.
Vercel
Crear un archivo vercel.json en la raíz:
{
"buildCommand": "npm run build",
"outputDirectory": "dist",
"rewrites": [
{ "source": "/(.*)", "destination": "/index.html" }
]
}
GitHub Pages
Configurar la base URL en vite.config.ts para que coincida con el nombre del repositorio:
export default defineConfig({
base: '/nombre-del-repo/',
// ...
})
Despliegue con Docker
Para entornos de producción con contenedores, se recomienda un Dockerfile multi-stage que separe la fase de build de la fase de servicio:
# Fase 1: Build
FROM node:20-alpine AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
# Fase 2: Servir con nginx
FROM nginx:alpine
COPY --from=build /app/dist /usr/share/nginx/html
COPY nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
Configuración de nginx para SPA
El archivo nginx.conf debe configurar el fallback a index.html para que Vue Router gestione todas las rutas:
server {
listen 80;
server_name localhost;
root /usr/share/nginx/html;
index index.html;
# Compresión gzip
gzip on;
gzip_types text/plain text/css application/json application/javascript text/xml;
gzip_min_length 256;
# Caché para assets estáticos con hash
location /assets/ {
expires 1y;
add_header Cache-Control "public, immutable";
}
# Fallback a index.html para rutas SPA
location / {
try_files $uri $uri/ /index.html;
}
}
Comandos para construir y ejecutar el contenedor:
docker build -t mi-app-vue .
docker run -p 8080:80 mi-app-vue
Sourcemaps en producción
Los sourcemaps permiten depurar el código original cuando se detectan errores en producción. La recomendación es usar sourcemap: 'hidden', que genera los archivos .map pero no incluye la referencia en los archivos JS:
export default defineConfig({
build: {
sourcemap: 'hidden',
},
})
De esta forma, los sourcemaps se pueden subir a un servicio de error tracking sin exponerlos a los usuarios finales.
Performance monitoring
Lighthouse
Lighthouse es una herramienta de Google integrada en Chrome DevTools que audita el rendimiento, accesibilidad, SEO y buenas prácticas de una página web. Las métricas clave para una SPA Vue son:
- First Contentful Paint (FCP): tiempo hasta que se pinta el primer contenido
- Largest Contentful Paint (LCP): tiempo hasta que se pinta el contenido más grande
- Cumulative Layout Shift (CLS): estabilidad visual durante la carga
- Total Blocking Time (TBT): tiempo que el hilo principal está bloqueado
Core Web Vitals
Los Core Web Vitals se pueden medir programáticamente con la API web-vitals:
npm install web-vitals
// src/vitals.ts
import { onLCP, onFID, onCLS } from 'web-vitals'
function sendToAnalytics(metric: { name: string; value: number }) {
console.log(`[${metric.name}]: ${metric.value}`)
// Enviar a servicio de analítica
}
onLCP(sendToAnalytics)
onFID(sendToAnalytics)
onCLS(sendToAnalytics)
Integración con Sentry para error tracking
Sentry permite capturar y rastrear errores en producción con contexto detallado:
npm install @sentry/vue
// main.ts
import { createApp } from 'vue'
import { createRouter, createWebHistory } from 'vue-router'
import * as Sentry from '@sentry/vue'
import App from './App.vue'
const app = createApp(App)
const router = createRouter({
history: createWebHistory(),
routes: [/* ... */],
})
Sentry.init({
app,
dsn: import.meta.env.VITE_SENTRY_DSN,
integrations: [
Sentry.browserTracingIntegration({ router }),
],
tracesSampleRate: 0.2,
environment: import.meta.env.MODE,
})
app.use(router)
app.mount('#app')
Sentry captura automáticamente errores no manejados, excepciones en componentes Vue y trazas de rendimiento de navegación. Los sourcemaps se pueden subir con @sentry/vite-plugin para obtener stack traces con el código original:
// vite.config.ts
import { sentryVitePlugin } from '@sentry/vite-plugin'
export default defineConfig({
build: {
sourcemap: 'hidden',
},
plugins: [
vue(),
sentryVitePlugin({
org: 'mi-organizacion',
project: 'mi-app-vue',
authToken: process.env.SENTRY_AUTH_TOKEN,
}),
],
})
Alan Sastre
Ingeniero de Software y formador, CEO en CertiDevs
Ingeniero de software especializado en Full Stack y en Inteligencia Artificial. Como CEO de CertiDevs, Vuejs es una de sus áreas de expertise. Con más de 15 años programando, 6K seguidores en LinkedIn y experiencia como formador, Alan se dedica a crear contenido educativo de calidad para desarrolladores de todos los niveles.
Más tutoriales de Vuejs
Explora más contenido relacionado con Vuejs y continúa aprendiendo con nuestros tutoriales gratuitos.
Aprendizajes de esta lección
Cursos que incluyen esta lección
Esta lección forma parte de los siguientes cursos estructurados con rutas de aprendizaje