wandres.dev
VITE · el dev server moderno

Crear un proyecto Vite y entender su estructura

El andamiaje de un proyecto Vite es deliberadamente pequeño: index.html como punto de entrada real, src para tu código, public para lo intocable y un puñado de comandos. Cada decisión de esa estructura codifica la filosofía del dev server.

⏱ 14 min

La estructura de un proyecto Vite parece trivial —cuatro carpetas y un par de archivos— pero cada pieza codifica una decisión de diseño. Que index.html viva en la raíz y sea el punto de entrada, que src y public traten los archivos de formas opuestas, que el flujo de comandos separe iterar de entregar: nada de esto es arbitrario. Montar bien el andamiaje no es ceremonia; es alinear tu proyecto con el modelo mental del dev server para que todo lo demás encaje sin fricción.

🎯 Al terminar esta lección sabrás
  • Crear un proyecto con el asistente y reconocer cada archivo que genera.
  • Entender por qué index.html es el punto de entrada y no un simple contenedor.
  • Distinguir el tratamiento de src frente al de public.
  • Dominar el flujo de comandos: crear, instalar, servir, construir, previsualizar.

Crear el proyecto

Vite se instala a través de un asistente que copia una plantilla y deja el proyecto listo. No necesitas instalarlo global: el comando create descarga la última versión y te pregunta el framework.

pnpm create vite@latest mi-app
# elige plantilla: vanilla, react, vue, svelte, solid...
cd mi-app
pnpm install
pnpm dev

El resultado es un árbol mínimo y legible de un vistazo. No hay decenas de archivos de configuración crípticos: la convención hace casi todo el trabajo.

mi-app/
  index.html          # punto de entrada: aqui empieza el grafo
  package.json        # scripts y dependencias
  vite.config.ts      # configuracion (plugins, alias, server)
  public/             # activos servidos tal cual, sin procesar
  src/
    main.tsx          # el modulo que arranca la aplicacion
    app.tsx
  node_modules/
    .vite/            # cache del pre-bundling de dependencias

Los scripts de package.json son el envoltorio con el que invocarás cada mitad de Vite sin recordar los comandos crudos.

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

index.html es el punto de entrada, no un adorno

Aquí Vite rompe con la tradición de webpack, y el detalle es más profundo de lo que parece. En los empaquetadores clásicos, el punto de entrada era un archivo JavaScript, y el HTML se generaba o inyectaba después mediante un plugin.

En Vite, index.html es código fuente de primera clase y es la raíz del grafo. Vite lo lee, encuentra la etiqueta <script type="module" src="/src/main.tsx"> y desde ahí desciende por todos los import.

<!DOCTYPE html>
<html lang="es">
  <head>
    <meta charset="UTF-8" />
    <title>Mi App</title>
  </head>
  <body>
    <div id="root"></div>
    <!-- este script es la puerta a todo el grafo de modulos -->
    <script type="module" src="/src/main.tsx"></script>
  </body>
</html>

Esta decisión tiene consecuencias prácticas. Puedes tener varios .html para una aplicación de múltiples páginas, cada uno un punto de entrada.

Y las rutas dentro del HTML se procesan: un src o un href que apunte a un archivo real se resuelve, se transforma y se versiona igual que cualquier módulo. El HTML deja de ser una plantilla ciega y se convierte en el mapa del proyecto.

src y public: dos clases de archivo

La distinción entre src y public confunde al principio, pero es nítida en cuanto ves el criterio: src es código que Vite procesa; public es material que Vite copia sin tocar.

🧩

src/

Todo lo que importas desde tu código. Vite lo transforma, le hace tree shaking, lo hashea y lo optimiza en el build.

📂

public/

Archivos servidos tal cual desde la raíz. No se procesan ni se hashean; se referencian por ruta absoluta como /logo.svg.

⚙️

vite.config.ts

El único config. Gobierna las dos mitades: plugins, alias de resolución, opciones del servidor y del build.

Un activo importado desde srcimport logo from "./logo.svg"— entra en el grafo: Vite lo optimiza, lo versiona con un hash de contenido y, si es pequeño, puede incrustarlo como data URI para ahorrar una petición.

Un archivo en public/robots.txt, en cambio, llega a dist/ intacto y con el mismo nombre, porque su URL debe ser estable y predecible. La regla mental es simple: si necesitas referenciarlo con una ruta fija y sin transformar, va en public; todo lo demás, en src.

El vite.config.ts es el tercer pilar. Aunque las plantillas lo generan casi vacío, es donde declaras plugins y alias de rutas, y ese archivo gobierna por igual el servidor y el build.

import { defineConfig } from "vite";

export default defineConfig({
  resolve: {
    alias: { "@": "/src" },   // importa con @/... en vez de rutas largas
  },
});

El flujo de comandos

Los tres comandos del ciclo de vida se corresponden con las dos mitades que ya conoces, más un puente entre ellas.

vite          # dev: servidor ESM instantaneo, con HMR
vite build    # prod: genera el bundle optimizado en dist/
vite preview  # sirve dist/ en local para verificar el resultado real
flowchart LR
A[create vite] --> B[install]
B --> C[vite dev]
C -->|iteras| C
C --> D[vite build]
D --> E[vite preview]
E --> F[deploy de dist]
style A fill:#cba6f7,color:#11111b
style C fill:#89b4fa,color:#11111b
style D fill:#a6e3a1,color:#11111b
style F fill:#fab387,color:#11111b

vite preview merece un aviso: sirve el contenido de dist/ como lo haría un servidor estático, pero no es un servidor de producción real ni sustituye al despliegue. Existe para que verifiques en local que el bundle funciona antes de subirlo. Confundir preview con producción es un error clásico de quien empieza.

💡
La raíz de las rutas absolutas es el proyecto, no public

Un tropiezo frecuente: en Vite, una ruta que empieza por / se resuelve desde la raíz del proyecto, no desde public. Los archivos de public acaban en la raíz de dist, así que a un public/logo.svg lo referencias como /logo.svg, sin nombrar la carpeta. Nombrarla —escribir /public/logo.svg— es un error que funciona en dev por casualidad y se rompe en producción, porque la carpeta public no existe en el output. Interioriza la regla desde el primer día y te ahorrarás una clase entera de enlaces rotos tras el despliegue.

La estructura es una tesis sobre la web

Que index.html sea el punto de entrada parece un detalle de fontanería, pero es una declaración de principios que conviene interiorizar. En el mundo de webpack, el HTML era un producto derivado: escribías JavaScript, y una herramienta fabricaba el HTML para colgarlo. El modelo mental empezaba en el módulo y trataba la página como un envoltorio. Vite invierte esa jerarquía y la alinea con la realidad: en la web, todo empieza cuando el navegador pide un documento HTML y descubre en él qué scripts cargar. Al hacer del HTML la fuente de la verdad, Vite hace que la estructura de tu proyecto refleje el orden real de carga de la plataforma, no una abstracción impuesta por la herramienta. Esto tiene un beneficio que va más allá de la comodidad: reduce la distancia entre tu modelo mental y lo que ocurre de verdad en producción, y esa distancia es donde se esconden los bugs. Cuando la estructura del proyecto coincide con la física de la web —documento primero, módulos después, activos referenciados con reglas claras— dejas de traducir entre dos mundos y empiezas a razonar directamente sobre uno. Un andamiaje pequeño y honesto no es minimalismo estético; es menos superficie donde equivocarse.

⚔️ Levanta el andamiaje y prueba sus reglas
  1. Crea un proyecto con pnpm create vite@latest, instálalo y arráncalo; abre index.html y localiza el <script type="module"> que es la raíz del grafo.
  2. Pon un archivo en public/ y otro equivalente en src/, impórtalos y compara en el build cuál recibe un hash en el nombre y cuál conserva su ruta.
  3. Añade un segundo .html en la raíz y comprueba que Vite lo trata como un punto de entrada independiente.
  4. Ejecuta vite build seguido de vite preview y confirma que lo que ves es el paquete real de dist/, no el servidor de desarrollo.