Capítulo 13 — Módulos y bundlers¶
Introducción¶
Durante años, todo el JavaScript de una página vivía en un solo archivo o se cargaba con múltiples etiquetas <script>. Aquello era un caos: colisiones de nombres, orden de carga frágil, cero encapsulación.
Los módulos resolvieron ese problema. Y los bundlers hacen que todo eso funcione de forma eficiente en producción.
¿Qué es un módulo?¶
Un módulo es un archivo que exporta ciertas cosas y oculta el resto. Solo lo que exportas es accesible desde fuera.
Exportar¶
// matematicas.js
export const PI = 3.1416;
export function sumar(a, b) {
return a + b;
}
export default function multiplicar(a, b) {
return a * b;
}
Importar¶
// app.js
import multiplicar, { PI, sumar } from './matematicas.js';
console.log(sumar(2, 3)); // 5
console.log(PI); // 3.1416
console.log(multiplicar(4, 5)); // 20
Export default vs named
- Named exports:
export function foo() {}→import { foo } from './x.js' - Default export:
export default function() {}→import cualquiera from './x.js'
Los named son más explícitos y permiten varios por archivo. Los default son cómodos para un único valor principal.
Módulos en el navegador¶
Añade type="module" a tu <script>:
<script type="module" src="app.js"></script>
Con eso ya puedes usar import / export sin bundler. Los navegadores modernos lo soportan.
Import dinámico¶
Carga módulos solo cuando los necesitas:
button.addEventListener('click', async () => {
const { abrirModal } = await import('./modal.js');
abrirModal();
});
Perfecto para code splitting y reducir el bundle inicial.
El problema: demasiados archivos¶
En un proyecto grande tienes cientos de módulos. Pedir cada uno por separado al servidor sería lentísimo. Aquí entran los bundlers.
¿Qué es un bundler?¶
Un bundler:
- Lee todos tus módulos.
- Resuelve las dependencias.
- Combina todo en uno o varios archivos.
- Optimiza: minifica, elimina código muerto, aplica tree shaking.
Bundlers populares:
| Bundler | Cuándo usarlo |
|---|---|
| Vite | Proyecto moderno, rápido, el estándar hoy |
| Webpack | Proyectos legacy, configuraciones muy complejas |
| Rollup | Librerías npm |
| esbuild | Herramienta rápida, base de Vite |
| Parcel | Zero-config, proyectos pequeños |
Vite paso a paso¶
Crear un proyecto¶
npm create vite@latest mi-app
cd mi-app
npm install
npm run dev
Vite te da:
- Servidor de desarrollo ultrarrápido (HMR).
- Soporte nativo de TypeScript, JSX, CSS modules…
- Build de producción optimizado.
Estructura típica¶
mi-app/
├── index.html
├── package.json
├── vite.config.js
└── src/
├── main.js
├── estilos.css
└── componentes/
package.json explicado¶
{
"name": "mi-app",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview"
},
"devDependencies": {
"vite": "^5.0.0"
}
}
Los scripts se ejecutan con npm run nombre.
npm run dev¶
Arranca el servidor de desarrollo en http://localhost:5173. Cada vez que guardas un archivo, el navegador se actualiza automáticamente.
npm run build¶
Genera la carpeta dist/ con todo optimizado:
- JavaScript minificado.
- CSS separado.
- Assets con hash (
app-a1b2c3.js).
Ese dist/ es lo que subes a producción.
npm: el gestor de paquetes¶
Comandos esenciales¶
npm init -y # crea package.json
npm install # instala todas las dependencias
npm install axios # añade una dependencia
npm install -D vitest # añade una dev-dependency
npm uninstall axios # la quita
npm run dev # ejecuta un script
npm outdated # muestra paquetes desactualizados
npm update # actualiza
dependencies vs devDependencies¶
- dependencies: código que usa tu app en producción (React, axios…).
- devDependencies: herramientas de desarrollo (Vite, ESLint, tests…).
package-lock.json¶
Registra las versiones exactas instaladas. Súbelo al repo. Garantiza que todo el equipo use las mismas.
.gitignore para proyectos Node¶
node_modules/
dist/
.env
.DS_Store
Nunca subas node_modules/. Es enorme y se regenera con npm install.
Variables de entorno con Vite¶
Crea un archivo .env:
VITE_API_URL=https://api.mi-app.com
Úsalo en código:
const api = import.meta.env.VITE_API_URL;
No metas secretos
Todo lo que empiece por VITE_ acaba en el bundle. Nunca pongas API keys privadas ahí. Los secretos van al backend.
Buenas prácticas¶
- Un archivo, una responsabilidad. Si un módulo hace demasiadas cosas, divídelo.
- Named exports por defecto. Son más fáciles de refactorizar.
- Rutas relativas o alias: configura
@/para evitar../../../. .gitignoresiempre connode_modules/ydist/.package-lock.jsonen Git. Es la garantía de reproducibilidad.- Actualiza dependencias con cabeza. Usa
npm auditpara vulnerabilidades. - Analiza tu bundle con
vite-bundle-visualizerorollup-plugin-visualizer.
Ejercicios¶
- Crea un proyecto con
npm create vite@latesty añade un módulo propio. - Exporta dos funciones y un valor con named exports, e impórtalos en
main.js. - Usa import dinámico para cargar un módulo solo al pulsar un botón.
- Añade
axioscomo dependencia yvitestcomo dev-dependency. - Configura un alias
@/que apunte asrc/envite.config.js. - Genera el build con
npm run buildy examina la carpetadist/.
⬅️ Capítulo 12 — JavaScript asíncrono | ➡️ Capítulo 14 — Accesibilidad web git add -A ; git commit -m "feat: capítulo 13 módulos y bundlers" ; git push