learning-front

Nivel 4 · Tooling profesional: el entorno de un proyecto serio

Vite: el servidor de desarrollo moderno

Arrancar un proyecto, el dev server con HMR, los builds y por qué es tan rápido: Rolldown, el bundler unificado en Rust de Vite 8.

Qué es Vite y por qué#

Ya conoces el bundler y el dev server del capítulo puente: un bundler junta y optimiza tus módulos, y el dev server arranca un servidor local con recarga en caliente mientras programas. Vite hace las dos cosas, pero de una forma que lo hace notablemente más rápido que sus predecesores.

Antes de Vite, la herramienta estándar era Webpack, el bundler que dominó el sector durante años. Webpack construye un grafo de módulos completo cada vez que arrancas: procesa todos los ficheros, resuelve todos los imports y genera un bundle antes de servir la primera página. En proyectos grandes eso se traduce en esperas de 30-60 segundos solo para ver la primera pantalla.

Vite, diseñado por Evan You (el creador de Vue), cambia la estrategia por completo. El resultado es un arranque casi instantáneo y actualizaciones que llegan al navegador en menos de un segundo.

El dev server: arranque instantáneo#

La clave está en cómo Vite sirve los ficheros en desarrollo.

Los navegadores modernos soportan ESM nativo: pueden cargar módulos directamente con <script type="module"> sin necesidad de que nadie los empaquete antes. Vite aprovecha esto:

javascript
// El navegador hace esta petición directamente:
// GET /src/main.js

// src/main.js — tu código tal cual, Vite solo añade transformaciones mínimas
// el navegador pide también /src/datos/heroes.js
import { heroes } from './datos/heroes.js';
// y /src/ui/tabla.js, y así hasta el final
import { renderTabla } from './ui/tabla.js';

// ejecuta la función que muestra los héroes en pantalla
renderTabla(heroes);

Cuando pides localhost:5173 en el navegador, Vite sirve el index.html tal cual. El navegador lee el <script type="module">, pide /src/main.js, y Vite solo transforma ese fichero concreto en el momento en que se pide. No hay bundle previo. El tiempo de arranque es independiente del tamaño del proyecto.

HMR: guardar y ver al instante#

Cuando guardas un fichero, Vite no recarga la página entera. En su lugar, usa HMR (Hot Module Replacement): sustituye solo el módulo que cambió en el navegador que ya tienes abierto.

La diferencia práctica:

  • Sin HMR: guardas, la página recarga, navegas de nuevo al estado que tenías.
  • Con HMR: guardas, el cambio aparece en 50-100 ms y normalmente sin perder el estado de la página.

Si estás probando el comportamiento de un héroe concreto en el Team Builder, no tienes que volver a buscarlo cada vez que ajustas un estilo o un cálculo.

Por qué vuela: Rolldown#

La velocidad de Vite no es solo la estrategia ESM en dev. También es el motor que hay debajo.

En Vite 8 (2026), hay un único bundler en Rust: Rolldown.

Rolldown se encarga de todo: de la transformación de módulos en desarrollo y del empaquetado para producción. Está escrito en Rust (a diferencia de JS o Go), lo que le da 10-30 veces más velocidad que los bundlers anteriores. Tiene un único sistema de plugins, compatible con el API que ya conocías de Rollup.

El minificador por defecto también es nuevo: oxc. Un minificador elimina espacios, comentarios y acorta nombres de variables para reducir el tamaño del JS final sin cambiar lo que hace. oxc lo hace en Rust, a una velocidad que los minificadores anteriores en JavaScript no pueden igualar. Por ahora basta saber que es quien comprime y optimiza el JS antes de meterlo en dist/.


Un poco de historia (para entender lo que verás en tutoriales viejos)

Vite no siempre usó un único bundler. Hasta Vite 7, la arquitectura era distinta:

  • Desarrollo: usaba esbuild, un transpilador escrito en Go muy rápido, para transformar cada módulo al vuelo.
  • Producción: usaba Rollup, un bundler centrado en la calidad del output, para generar el build final.

Rollup es un bundler de JavaScript especializado en producir código limpio y bien optimizado: analiza el grafo de módulos, elimina el código que nadie usa (tree-shaking) y genera chunks lo más pequeños posible. Su sistema de plugins fue tan bien diseñado que se convirtió en el estándar de facto para extender bundlers: Vite, Rolldown y muchas otras herramientas lo adoptaron o lo mantienen compatible. Por eso, si lees documentación antigua de Vite o algún plugin externo, verás referencias a la API de Rollup — esas mismas opciones siguen funcionando en su mayoría.

Dos herramientas distintas significaban posibles diferencias entre lo que veías en dev y lo que salía en prod. Vite 8 lo resuelve con Rolldown: mismo motor para los dos entornos.

Si ves configuración así en un proyecto existente:

javascript
// vite.config.js de Vite 7 o anterior — ya está deprecado
export default defineConfig({
  build: {
    // <-- en Vite 8 se llama rolldownOptions
    rollupOptions: {
      output: {
        manualChunks: { vendor: ['react', 'react-dom'] },
      },
    },
  },
});

En Vite 8 el campo correcto es rolldownOptions, no rollupOptions. La lógica es la misma; solo cambia el nombre para reflejar que quien hace el trabajo ahora es Rolldown.


La estructura de un proyecto Vite#

Cuando andamias un proyecto con npm create vite, obtienes esta estructura:

texto
team-builder/
├── index.html          ← el ENTRY: Vite parte de aquí, no de un JS suelto
├── vite.config.js      ← configuración de Vite (puedes crearlo si no existe)
├── package.json        ← manifiesto: scripts y dependencias
├── src/
│   └── main.js         ← tu código (desde aquí tiras los imports)
└── public/
    └── favicon.ico     ← ficheros que se sirven tal cual, sin pasar por Vite

index.html: el entry real#

En Webpack el entry era un fichero JS. En Vite es el index.html. Vite lo lee, encuentra el <script type="module"> y sigue el grafo de imports desde ahí:

html
<!doctype html>
<html lang="es">
  <head>
    <meta charset="UTF-8" />
    <title>Team Builder</title>
  </head>
  <body>
    <div id="app"></div>
    <!-- El entry: Vite sigue este script y sirve sus imports al navegador -->
    <script type="module" src="/src/main.js"></script>
  </body>
</html>

src/: tu código#

Todo lo que importas desde tus módulos vive aquí. Vite lo procesa cuando el navegador lo pide (en dev) o lo empaqueta todo (en build).

public/: ficheros sin procesar#

Los ficheros de public/ se copian a dist/ tal cual, sin transformaciones. Úsala para:

  • El favicon.ico o iconos PWA
  • Un robots.txt
  • Imágenes que referencias por URL en CSS (no por import en JS)

Si importas una imagen en JS (import logoUrl from './logo.png'), no va en public/: va en src/ y Vite la gestiona, le añade hash para caché y la optimiza.

import.meta: datos del entorno#

Vite expone información del entorno a través de import.meta. El más usado es import.meta.env, que da acceso a las variables de entorno. Lo vemos en el capítulo 4.

javascript
// Adelanto: en el capítulo 4 aprenderás a usar import.meta.env
// Por ahora, solo para que sepas que existe:
// "development" o "production"
console.log('Modo: ' + import.meta.env.MODE);

vite.config#

El fichero vite.config.js en la raíz del proyecto configura Vite. No es obligatorio: si no existe, Vite usa sus valores por defecto (dev en el puerto 5173, sin alias). Pero en cualquier proyecto real lo querrás.

La forma más común usa defineConfig, un helper de Vite que activa el autocompletado en el editor:

javascript
// vite.config.js — configuración del proyecto Team Builder
// helper que activa autocompletado y comprobación de tipos
import { defineConfig } from 'vite';

export default defineConfig({
  server: {
    // el dev server escuchará en http://localhost:3000
    // (por defecto: 5173; fijarlo evita conflictos entre proyectos)
    port: 3000,
  },
  resolve: {
    alias: {
      // import { heroes } from '@/datos/heroes' apunta a src/datos/heroes
      // sin tener que contar ../.. según lo anidado que estés
      '@': '/src',
    },
  },
});

Con el alias @ puedes importar así desde cualquier fichero, sin importar en qué carpeta estés:

javascript
// Antes, desde src/ui/tabla.js, tenías que escribir:
// dos niveles arriba
import { heroes } from '../../datos/heroes.js';

// Con el alias @, desde cualquier sitio:
// siempre la misma ruta
import { heroes } from '@/datos/heroes.js';

Si usas TypeScript, el alias también necesita declararse en tsconfig.json. El capítulo de TypeScript lo cubre.

dev, build, preview#

Un proyecto Vite generado con npm create vite trae estos tres scripts en su package.json:

json
{
  "scripts": {
    "dev":     "vite",
    "build":   "vite build",
    "preview": "vite preview"
  }
}

npm run dev#

Arranca el servidor de desarrollo. No genera ningún fichero: todo vive en memoria y se sirve bajo demanda. Tiene HMR activo. Es lo que usas mientras programas.

npm run build#

Genera la carpeta dist/ con el proyecto listo para producción. Es Rolldown —el mismo motor en Rust del que hablábamos antes— quien procesa todo el grafo de módulos, resuelve los imports, aplica optimizaciones y deja que oxc minifique el JS. El resultado:

texto
dist/
├── index.html            ← HTML con rutas a los assets hasheados
└── assets/
    ├── index-Ce4KZ7bm.js  ← bundle JS minificado (el hash cambia con el contenido)
    └── index-BQ3FMZQB.css ← CSS empaquetado

El hash en el nombre del fichero es intencional: si el contenido cambia, cambia el hash, y el navegador descarga la versión nueva en vez de usar la caché.

npm run preview#

Sirve dist/ con un servidor local. Úsalo antes de desplegar para comprobar que el build funciona igual que el dev server. Es fácil que algo funcione en dev (ESM directo) pero falle en prod (bundle), especialmente con rutas de assets o rutas absolutas mal puestas.

shell
# genera dist/
npm run build
# sirve dist/ en http://localhost:4173 para revisar
npm run preview

Comprueba lo que sabes#

Pregunta 1 de 5

¿Por qué el dev server de Vite arranca casi al instante?

Tu turno#

Este ejercicio se hace en local: abre el Team Builder que tienes de los capítulos anteriores y practica el ciclo completo con Vite. Cuando termines, despliega las soluciones y compara.

Ejercicio · hazlo en local

Configura y domina tu proyecto Vite

Sobre el Team Builder con Vite que llevas del Nivel 3, practica el ciclo completo: arranca el dev server, comprende la estructura, configura el proyecto con alias y puerto fijo, y si llegas al nivel excelente, añade code splitting para que los módulos pesados no bloqueen la carga inicial.

Paso 1: Que funcione

  • Arrancas el proyecto con `npm run dev` y el navegador abre en `localhost:5173`.
  • Editas un fichero en `src/` y ves el cambio reflejado en el navegador sin recargar.
  • Localizas los tres directorios clave: `index.html` (entry), `src/` y `public/`.
  • Generas el build de producción con `npm run build` y compruebas que existe `dist/`.

Cómo hacerlo en local

Clona el repositorio del curso, entra en la carpeta del ejercicio y abre el index.html en tu navegador. Toda tu solución va en solucion.js.

git clone <repo>
cd exercises/nivel-4/vite
# abre index.html en el navegador y edita solucion.js
Ver soluciones
# OK — arrancar el Team Builder con Vite y entender su estructura.

# arranca el dev server en http://localhost:5173 (recarga en caliente)
npm run dev

# Estructura clave del proyecto Vite:
#   index.html  -> el ENTRY: el bundler parte de aquí (no de un main.js suelto)
#                  busca el <script type="module" src="/src/main.js"> dentro del HTML
#   src/        -> tu código fuente (main.js, componentes, módulos)
#   public/     -> ficheros que se sirven tal cual, sin pasar por el bundler
#                  (favicon.ico, robots.txt, imágenes estáticas que no importas)

# genera dist/ con el código optimizado para producción
npm run build

# dist/ tendrá algo como:
#   dist/index.html        -> el HTML con las rutas a los assets hasheados
#   dist/assets/index-abc.js -> el bundle JS minificado (Rolldown + oxc)
#   dist/assets/index-xyz.css -> el CSS empaquetado

# sirve dist/ en local para revisar el build antes de desplegar
# útil para comprobar que el build se ve igual que en dev
npm run preview

Por qué este nivel

  • Arrancas con el dev server y ves la recarga en caliente: el ciclo guardar-y-ver-al-instante. Es la base de trabajar con Vite.
  • Entiendes la estructura: index.html como entry, src/ para tu código, public/ para lo que no debe procesarse.
  • npm run build genera dist/: ya sabes cómo se ve el resultado antes de desplegar.