Módulos
Antes de los módulos, todo el JavaScript de una página compartía un único ámbito global. Dos scripts con una variable usuario colisionaban silenciosamente. Los módulos ES solucionan esto: cada archivo tiene su propio ámbito y controla explícitamente qué expone.
export nombrado
Exporta uno o varios valores por nombre. El importador debe usar el mismo nombre (o un alias):
// utils/matematicas.js
export function sumar(a, b) {
return a + b;
}
export function restar(a, b) {
return a - b;
}
export const PI = 3.14159;
// Importar solo lo que necesitas
import { sumar, PI } from './utils/matematicas.js';
console.log(sumar(2, 3)); // 5
console.log(PI); // 3.14159
// Con alias
import { sumar as add } from './utils/matematicas.js';
add(2, 3);
export default
Cada módulo puede tener un único export default. No necesita nombre al importarlo:
// componentes/Boton.js
export default function Boton({ texto, onClick }) {
const btn = document.createElement('button');
btn.textContent = texto;
btn.addEventListener('click', onClick);
return btn;
}
// Importar default — el nombre lo eliges tú
import Boton from './componentes/Boton.js';
import MiBoton from './componentes/Boton.js'; // mismo módulo, nombre distinto
// Combinar default y nombrados en un import
import Boton, { TAMANIOS } from './componentes/Boton.js';
Re-exportar
Útil para crear un barrel file que centralice las exportaciones de un directorio:
// utils/index.js — punto de entrada del directorio
export { sumar, restar } from './matematicas.js';
export { formatearFecha } from './fechas.js';
export { validarEmail } from './validaciones.js';
// O re-exportar todo
export * from './matematicas.js';
// Re-exportar el default con nombre
export { default as Boton } from './componentes/Boton.js';
// Ahora el importador usa un solo import
import { sumar, formatearFecha, Boton } from './utils/index.js';
Importaciones y el ámbito de módulo
// Los módulos tienen su propio ámbito — esto es privado
const conexionBD = crearConexion(); // no se puede acceder desde fuera
// Solo lo exportado es accesible
export function buscarUsuario(id) {
return conexionBD.query(`SELECT * FROM usuarios WHERE id = ?`, [id]);
}
Las importaciones son vistas en vivo del valor exportado, no copias. Si el módulo exportador cambia el valor, el importador ve el cambio:
// contador.js
export let cuenta = 0;
export function incrementar() { cuenta++; }
// main.js
import { cuenta, incrementar } from './contador.js';
console.log(cuenta); // 0
incrementar();
console.log(cuenta); // 1 — actualizado automáticamente
Importación dinámica
A veces no sabes qué módulo cargar hasta el momento de ejecución, o quieres cargar código solo cuando se necesita (lazy loading):
// import() es asíncrono — devuelve una promesa
async function cargarGrafica() {
// El módulo no se descarga hasta que se llama a esta función
const { dibujarGrafica } = await import('./lib/grafica.js');
dibujarGrafica(datos);
}
// Cargar según la acción del usuario
btn.addEventListener('click', async () => {
const modulo = await import('./modal.js');
modulo.abrirModal();
});
Esto es fundamental en frameworks como Astro, React o Vue para dividir el bundle en trozos menores (code splitting).
Módulos en el navegador
<!-- Atributo type="module" activa el sistema de módulos -->
<script type="module" src="./main.js"></script>
<!-- Las importaciones dentro del script también funcionan -->
<script type="module">
import { saludar } from './utils.js';
saludar('mundo');
</script>
Diferencias clave con scripts normales:
- Tienen
deferpor defecto (no bloquean el HTML) - Tienen su propio ámbito — no contaminan
window - Las rutas deben ser explícitas (
./utils.js, noutils) - Se cachean por separado en el navegador
Módulos en Node.js
Node usa módulos CommonJS (require/module.exports) por defecto. Para usar ES Modules:
// package.json
{
"type": "module"
}
O usando la extensión .mjs en lugar de .js.
// ES Module en Node
import { readFile } from 'node:fs/promises';
const contenido = await readFile('./datos.txt', 'utf-8');
// CommonJS — sigue siendo válido en archivos .cjs
const { readFileSync } = require('fs');
Convenciones
src/
utils/
fechas.js # funciones de fecha — export nombrado
validaciones.js # validadores — export nombrado
index.js # barrel file — re-exporta todo
componentes/
Boton.js # componente — export default
Modal.js
servicios/
api.js # capa de comunicación con el servidor
- Funciones utilitarias → export nombrado
- Clases o componentes únicos → export default
- Un directorio grande → barrel file con
index.js
En la siguiente lección dominamos la desestructuración y el spread: extraer valores de objetos y arrays con claridad.