Ir al contenido
wannacode

Del sandbox al proyecto

En resumen: Vite convierte los componentes en un proyecto real: npm create vite crea las carpetas, npm run build monta dist/ para publicar, y las variables de entorno VITE_ se integran en el código del sitio, así que son públicas.

Problema

Funciona en el sandbox, no en el navegador

Tu tienda funciona en el sandbox. Copias los archivos a una carpeta, la sirves y abres la página. Está vacía, y la consola dice:

Uncaught TypeError: Failed to resolve module specifier "vue". Relative references must start with either "/", "./", or "../".

import { createApp } from 'vue' no es la ruta de un archivo, y el navegador no sabe dónde está Vue. Y App.vue no es JavaScript en absoluto: el navegador no entiende <template> ni <script setup>.

El sandbox de las lecciones hacía este trabajo por ti. Traduce los archivos .vue a JavaScript en la página de la lección y sabe de dónde sacar la biblioteca. En un proyecto real lo hace una herramienta.

La idea

Vite: un servidor de desarrollo y un build

Vite es la herramienta con la que se crean la mayoría de los proyectos nuevos de React y Vue. Un proyecto vacío cuesta un comando:

Terminal
npm create vite@latest shop -- --template vue
cd shop
npm install
npm run dev

--template elige la plantilla: vue o vue-ts (con TypeScript). Los dos guiones -- de antes le dicen a npm que la opción es para create-vite, no para npm. npm install descarga las bibliotecas a una carpeta node_modules.

Vite tiene dos trabajos, y package.json los da como scripts:

  • npm run dev arranca un servidor de desarrollo en http://localhost:5173. Traduce cada archivo cuando el navegador lo pide. Guardas un archivo, y la página se actualiza enseguida.
  • npm run build crea el sitio terminado en una carpeta dist/. Es el build: todo traducido y unido en archivos normales.
  • npm run preview sirve dist/ en http://localhost:4173, para que veas el build antes de publicarlo.

La idea

Las carpetas y las reglas

La plantilla te da poco. Así queda la misma tienda cuando crece:

shop/
index.html la entrada: <script type="module" src="/src/main.js">
package.json las bibliotecas y los scripts dev, build, preview
vite.config.js los ajustes de Vite (el plugin de Vue)
public/ archivos que se copian tal cual (favicon.svg)
src/
main.js createApp(App).use(router).use(pinia).mount('#app')
App.vue
components/ ProductCard.vue, CartSummary.vue
pages/ CatalogPage.vue, ProductPage.vue (para el router)
stores/ cart.js: el store de Pinia
api/ products.js: las funciones con fetch
assets/ imágenes que el código trae con import

index.html, public/ y src/main pertenecen a Vite. Las otras carpetas son un acuerdo del equipo, no una regla: Vite no las necesita. El acuerdo ayuda a encontrar las cosas sin buscar. Los tests de la lección anterior van junto al componente: ProductCard.test.<Only when="react">jsx</Only><Only when="vue">js</Only>.

Linter y formato. Un linter lee el código y detecta errores antes de ejecutarlo: un hook dentro de un if, una variable olvidada. El formato (por ejemplo con Prettier) ordena espacios y comillas igual para todo el equipo, y así nadie discute por eso en una revisión de código. La plantilla de Vite para Vue no trae linter. La forma oficial de empezar un proyecto de Vue, npm create vue@latest, te pregunta qué quieres: TypeScript, Router, Pinia, Vitest, Playwright, ESLint u Oxlint, Prettier.

Míralo

Qué hace el build

Cómo unos archivos con .vue e import se convierten en un sitio que el navegador entiende.

import { createApp } from 'vue';
import App from './App.vue';
import './style.css';
createApp(App).mount('#app');
// App.vue
import ProductCard from './components/ProductCard.vue';
import hero from './assets/hero.png';
// npm run build → dist/
// dist/index.html
// dist/assets/index-[hash].js
// dist/assets/index-[hash].css
// dist/assets/hero-[hash].png
Paso a paso, en palabras
  1. El build empieza en index.html. Este carga main.js con <script type="module">, y ahí empieza la aplicación.
  2. Vite sigue cada import, archivo por archivo: App, ProductCard, el CSS, la imagen. Un archivo que nadie importa no entra.
  3. Todo el JavaScript, con la biblioteca de node_modules incluida, se une en un solo archivo y se reduce: sin espacios, con nombres cortos.
  4. Salen archivos normales que el navegador entiende: un .js, un .css y la imagen. Cada nombre lleva un hash calculado a partir del contenido.
  5. dist/index.html ahora apunta a los nombres nuevos. Toda la carpeta dist/ es el sitio: la publicas tal como está.
  6. Cambias ProductCard y construyes de nuevo. El JavaScript es distinto, así que su nombre es nuevo. El CSS no cambió y conserva su nombre.
  7. Por eso el navegador puede conservar un archivo con hash para siempre: una versión nueva siempre tiene un nombre nuevo. Los visitantes descargan solo lo que cambió.

dist/ es el sitio terminado: un archivo HTML y una carpeta assets/. No necesita un servidor, solo un hosting que reparta archivos. No está en git (la plantilla lo pone en .gitignore): lo generas de nuevo en cada versión.

La idea

TypeScript en los componentes

En el sandbox puedes pasarle a ProductCard cualquier cosa: product={42}. El error solo aparece al dibujar. TypeScript dice qué acepta el componente y detecta el error en el editor, antes de ejecutar nada:

<script setup lang="ts">
import type { Product } from '../types';
const props = defineProps<{ product: Product }>();
const emit = defineEmits<{ add: [product: Product] }>();
</script>

lang="ts" activa TypeScript en el archivo .vue. defineProps<{ … }>() describe las props con un tipo en lugar de un array de nombres, y defineEmits describe qué lleva cada evento. Si App pasa price: '1.20', la comprobación dice Type 'string' is not assignable to type 'number'.

Hay algo que sorprende: Vite solo quita los tipos, no los comprueba. Eso lo mantiene rápido. La comprobación va aparte: en la plantilla de TypeScript npm run build es vue-tsc -b && vite build. Si los tipos están mal, el build se detiene antes de que arranque Vite.

Pruébalo

Encuentra el error

El editor subraya una línea en rojo. ¿Cuál?

Elige la línea con el error.

La idea

Las variables de entorno son públicas

La dirección de la API es distinta en tu computadora y en el sitio real. Por eso no se escribe en el código, sino en un archivo .env en la raíz del proyecto:

Terminal
VITE_API_URL=https://api.shop.example
const API_URL = import.meta.env.VITE_API_URL;

Vite le da al código solo las variables que empiezan por VITE_. Con npm run build escribe el valor en el JavaScript: dist/assets/index-….js contiene https://api.shop.example como texto normal. Cualquiera que abra el sitio puede leerlo en las herramientas del navegador.

Por eso en una variable VITE_ nunca pones un secreto: una clave de pagos, la contraseña de la base de datos, una clave de API que cuesta dinero. No importa que .env no esté en git. El secreto está en el bundle. Los secretos viven en el servidor, y la página envía una petición a tu API (la lección sobre secretos de «Seguridad para programadores»).

Pruébalo

Encuentra el error

La función paga un pedido. ¿Qué línea deja salir un secreto al mundo?

Elige la línea con el error.

La idea

Publicar y las direcciones del router

dist/ son solo archivos, así que sirve cualquier hosting para sitios estáticos. Publicar es: npm run build y copiar dist/. Muchos hostings lo hacen por ti en cada push a git.

Queda un detalle de la lección de rutas. El sitio real usa createWebHistory(), y las direcciones se ven como /product/7. Un cliente abre una dirección así directamente (desde un enlace de un correo o al recargar). El navegador le pide al hosting /product/7, y dist/ no tiene ese archivo: solo index.html y assets/. El hosting responde 404.

La solución es una regla en el hosting: toda dirección que no sea un archivo devuelve index.html. La aplicación arranca, y el router lee la dirección y dibuja ProductPage. Esto se llama SPA fallback. Cada hosting lo escribe a su manera, por ejemplo en nginx:

location / {
try_files $uri $uri/ /index.html;
}

npm run dev y npm run preview lo hacen solos, por eso el error se ve solo en el hosting real. Compruébalo justo después de la primera publicación: abre una dirección de producto directamente.

La idea

El siguiente paso: Next.js y Nuxt

Todo lo de este curso es una aplicación en el navegador: el servidor entrega un index.html vacío y JavaScript, y la página se dibuja en el lado del cliente. Hasta que el JavaScript llega y arranca, el cliente no ve nada.

El renderizado en el servidor (server-side rendering, SSR) dibuja el primer HTML en el servidor. La página se ve antes, y los buscadores la leen sin JavaScript. Después los mismos componentes se despiertan en el navegador y se vuelven interactivos. Esto es lo que hacen los frameworks sobre las bibliotecas: Nuxt. Añaden también rutas por archivos y código que se ejecuta solo en el servidor (ahí pueden vivir los secretos).

Los componentes, las props, el estado y los efectos son los mismos. Así que este curso es la base, y el framework es la siguiente lección que aprenderás por tu cuenta.

Más precisamente, para curiosos

El servidor de desarrollo no empaqueta. Con npm run dev el navegador carga cada módulo por separado, y Vite lo traduce cuando se lo piden. Por eso arranca en un momento incluso en un proyecto grande. Unir todo en un archivo ocurre solo en el build.

Las imágenes pequeñas se incrustan. Una imagen de menos de 4 KB traída con import no se convierte en archivo: Vite la mete en el JavaScript como una dirección data: (el ajuste es build.assetsInlineLimit). Los archivos de public/ nunca se tocan y no reciben hash, así que los citas con la ruta completa: /favicon.svg.

Los archivos .env. .env.local es solo para tu computadora (la plantilla lo deja fuera de git con *.local). .env.production vale para el build, y .env.development para npm run dev. import.meta.env.DEV e import.meta.env.PROD dicen en qué modo está el código. Todos los valores son texto: VITE_PAGE_SIZE=20 da '20', no 20.

Un sitio en una subcarpeta. Si el sitio vive en https://example.com/shop/, Vite tiene que saberlo: base: '/shop/' en vite.config.js. Si no, index.html apunta a /assets/… en la raíz y los archivos no se encuentran.

npm run preview no es para el sitio real. Sirve para comprobar el build en tu máquina. El sitio real lo sirve un hosting o un servidor como nginx.

Ejercicio

De una carpeta vacía a un build

Pon los comandos en orden: un proyecto nuevo, las bibliotecas, el servidor de desarrollo, el build y la comprobación del build.

  1. npm run build
  2. npm create vite@latest shop -- --template vue
  3. npm run preview
  4. npm run dev
  5. npm install
  6. cd shop

Test

Ponte a prueba

1. ¿Qué hace npm run build en un proyecto de Vite?
2. ¿Por qué los nombres de dist/assets/ llevan un hash (index-DSXtc1GT.js)?
3. La clave de pago está en VITE_PAYMENT_KEY dentro de .env, y .env no está en git. ¿Es un secreto?
4. El sitio está publicado. Un cliente abre /product/7 directamente y el hosting responde con un 404. ¿Qué falta?
5. El proyecto usa TypeScript. vite build termina bien, y npm run build falla con un error de tipos. ¿Por qué?

Creado por Ivo Gergov como parte de la campaña de Evo Tech por una educación accesible en programación ·