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 SyntaxError: Unexpected token '<'El navegador entiende JavaScript, y <App /> es JSX. Incluso sin JSX viene otro error: import { createRoot } from 'react-dom/client' no es la ruta de un archivo, y el navegador no sabe dónde está react-dom.
El sandbox de las lecciones hacía este trabajo por ti. Traduce el JSX 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:
npm create vite@latest shop -- --template reactcd shopnpm installnpm run dev--template elige la plantilla: react o react-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 devarranca un servidor de desarrollo enhttp://localhost:5173. Traduce cada archivo cuando el navegador lo pide. Guardas un archivo, y la página se actualiza enseguida.npm run buildcrea el sitio terminado en una carpetadist/. Es el build: todo traducido y unido en archivos normales.npm run previewsirvedist/enhttp://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.jsx"> package.json las bibliotecas y los scripts dev, build, preview vite.config.js los ajustes de Vite (el plugin de React) public/ archivos que se copian tal cual (favicon.svg) src/ main.jsx createRoot(…).render(<App />) App.jsx components/ ProductCard.jsx, CartSummary.jsx pages/ CatalogPage.jsx, ProductPage.jsx (para el router) context/ CartContext.jsx (el estado compartido) api/ products.js: las funciones con fetch assets/ imágenes que el código trae con importindex.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 React trae un linter: npm run lint ejecuta oxlint con la regla de los hooks.
Míralo
Qué hace el build
Cómo unos archivos con JSX e import se convierten en un sitio que el navegador entiende.
import { createRoot } from 'react-dom/client';import App from './App.jsx';import './index.css';
createRoot(document.getElementById('root')).render(<App />);
// App.jsximport ProductCard from './components/ProductCard.jsx';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].pngPaso a paso, en palabras
- El build empieza en index.html. Este carga main.jsx con <script type="module">, y ahí empieza la aplicación.
- Vite sigue cada import, archivo por archivo: App, ProductCard, el CSS, la imagen. Un archivo que nadie importa no entra.
- Todo el JavaScript, con la biblioteca de node_modules incluida, se une en un solo archivo y se reduce: sin espacios, con nombres cortos.
- Salen archivos normales que el navegador entiende: un .js, un .css y la imagen. Cada nombre lleva un hash calculado a partir del contenido.
- dist/index.html ahora apunta a los nombres nuevos. Toda la carpeta dist/ es el sitio: la publicas tal como está.
- Cambias ProductCard y construyes de nuevo. El JavaScript es distinto, así que su nombre es nuevo. El CSS no cambió y conserva su nombre.
- 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:
type Product = { id: number; name: string; price: number };
type Props = { product: Product; onAdd: (product: Product) => void;};
export default function ProductCard({ product, onAdd }: Props) { // …}El archivo es .tsx. Si App olvida onAdd, el editor dice Property 'onAdd' is missing in type … but required in type 'Props'.
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 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.
Línea 12: product.prise. El tipo Product no tiene ese campo, y TypeScript dice Property 'prise' does not exist on type 'Product'. Did you mean 'price'?. Sin TypeScript product.prise es undefined, y el precio equivocado se ve solo en la pantalla.
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:
VITE_API_URL=https://api.shop.exampleconst 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.
Línea 2: la clave de pago está en una variable VITE_, así que está en el JavaScript del sitio. Cualquiera puede cogerla y pagar en tu nombre. La página envía orderId a tu API, y el servidor llama al servicio de pagos con la clave que solo él conoce.
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 BrowserRouter, 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: Next.js. 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.
StrictMode en main.jsx. La plantilla envuelve <App /> en <StrictMode>. En desarrollo renderiza cada componente dos veces, y ejecuta los efectos, los limpia y los vuelve a ejecutar, para sacar a la luz los efectos sin limpieza. El build no hace esto.
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.
npm run buildcd shopnpm run devnpm create vite@latest shop -- --template reactnpm installnpm run preview
Test
Ponte a prueba
Lecciones relacionadas
Sección «Lecciones relacionadas»- Se basa en: Datos de una API en un componente · Estado compartido · Enrutamiento · Probar componentes
- Continúa en: El escaparate de la tienda
- Ejercicios sobre el tema: Una aplicación completa