WEB ATELIER (UDIT) · Aprender haciendo, con teoría, práctica y reflexión compartida

Módulos JavaScript: Del Caos a la Modularidad

URL: https://ruvebal.github.io/web-atelier-udit/lessons/es/js-modules/

📋 Tabla de Contenidos

🎯 Objetivos de Aprendizaje

  • Comprender la evolución histórica de la modularización en JavaScript desde scripts globales hasta ES6 Modules
  • Dominar la sintaxis import/export incluyendo exportaciones por defecto y nombradas
  • Aplicar patrones modulares para crear aplicaciones web escalables y mantenibles
  • Conectar teoría con práctica mediante ejercicios prácticos siguiendo la metodología Atelier
  • Reconocer los principios que guían la arquitectura moderna de JavaScript

📜 La Evolución: Del Caos Global a la Ilustración Modular

“La historia de la programación es la historia de gestionar la complejidad. Los módulos son nuestra salvación.” — Kyle Simpson, You Don’t Know JS (inspirado)

1995-2009: La Era de las Variables Globales

En el principio, JavaScript no tenía sistema de módulos. Cada script compartía el mismo espacio de nombres global:

<!-- index.html -->
<script src="utils.js"></script>
<script src="app.js"></script>
<script>
  // Todo vive en el scope global
  var message = 'Hola Mundo'; // Contamina el namespace global
  
  // ¿Qué pasa si utils.js también declara 'message'? 💥 ¡Colisión!
</script>

Los Problemas:

  • 🔥 Contaminación del namespace - Variables en conflicto
  • 🔥 Sin encapsulación - Todo es público
  • 🔥 Dependencias implícitas - El orden de carga importa pero no es explícito
  • 🔥 Sin reutilización de código - Copy-paste era la norma

Patrón 1: IIFE (Immediately Invoked Function Expression)

La primera solución de la comunidad fue el Patrón Módulo usando IIFEs:

// utils.js - circa 2006
var Utils = (function() {
  // Variables privadas (ocultas en closure)
  var privateSecret = 'Esto es privado';
  
  // API pública
  return {
    add: function(a, b) {
      return a + b;
    },
    getSecret: function() {
      return privateSecret;
    }
  };
})();

// Uso
Utils.add(2, 3); // ✅ 5
Utils.privateSecret; // ❌ undefined - ¡encapsulado!

Lo que cambió:

  • Encapsulación mediante closures
  • API pública explícita
  • Reducción de contaminación global
  • ❌ Aún gestión manual de dependencias
  • ❌ Sin estandarización

“El patrón IIFE fue un hack—uno brillante—pero un hack al fin. Lo necesitábamos porque el lenguaje carecía de modularidad integrada.” — Douglas Crockford, JavaScript: The Good Parts (inspirado)

2009: CommonJS - Módulos para el Lado del Servidor

Cuando Node.js emergió en 2009, adoptó el sistema de módulos CommonJS:

// math.js (CommonJS)
function add(a, b) {
  return a + b;
}

function multiply(a, b) {
  return a * b;
}

// Exportar API
module.exports = {
  add: add,
  multiply: multiply
};

// O forma corta:
exports.add = add;
exports.multiply = multiply;
// app.js
const math = require('./math.js');

console.log(math.add(2, 3)); // 5
console.log(math.multiply(4, 5)); // 20

Características:

  • Carga síncrona - Perfecto para servidor (sistema de archivos)
  • Dependencias explícitas - require() lo deja claro
  • Objeto de exportación único - module.exports
  • No nativo en navegadores - Necesita bundlers (Browserify, Webpack)

“CommonJS trajo orden al JavaScript del lado del servidor. Por primera vez teníamos una forma estándar de organizar código.” — Ryan Dahl, creador de Node.js (inspirado)

2011: AMD (Asynchronous Module Definition)

Para navegadores, RequireJS introdujo AMD para carga asíncrona:

// math.js (AMD)
define(['dependency1', 'dependency2'], function(dep1, dep2) {
  function add(a, b) {
    return a + b;
  }
  
  // Retornar API pública
  return {
    add: add
  };
});
// app.js
require(['math'], function(math) {
  console.log(math.add(2, 3)); // 5
});

Características:

  • Carga asíncrona - No bloqueante para navegadores
  • Gestión de dependencias - Declaradas por adelantado
  • Sintaxis verbosa - Overhead de envoltorio
  • Diferente de CommonJS - Ecosistema dividido

2015: Módulos ES6 - El Estándar Nativo

ECMAScript 2015 (ES6) finalmente introdujo módulos nativos:

// math.js (Módulos ES6)
export function add(a, b) {
  return a + b;
}

export function multiply(a, b) {
  return a * b;
}

// Exportación por defecto
export default function subtract(a, b) {
  return a - b;
}
// app.js
import subtract, { add, multiply } from './math.js';

console.log(add(2, 3)); // 5
console.log(multiply(4, 5)); // 20
console.log(subtract(10, 3)); // 7

Por Qué Ganaron los Módulos ES6:

  • Soporte nativo en navegadores - Sin bundler necesario (con matices)
  • Análisis estático - Las herramientas pueden optimizar (tree-shaking)
  • Sintaxis más limpia - Más legible
  • Async por defecto - <script type="module">
  • Modo estricto - Código más seguro automáticamente

“Los Módulos ES6 representan la culminación del viaje de JavaScript hacia la madurez. Son el estándar que estábamos esperando.” — Axel Rauschmayer, Exploring ES6 (inspirado)

Visualización de la Línea Temporal

1995 ━━━━━━━━━━━━━━━━━━━━━━━━ Era del Scope Global
     │                          • Sin módulos
     │                          • Variables globales por doquier
     │                          • Sopa de tags <script>
     │
2006 ━━━━━━━━━━━━━━━━━━━━━━━━ Patrón Módulo (IIFE)
     │                          • Closures para encapsulación
     │                          • Aún gestión manual de dependencias
     │                          • Patrón de diseño, no estándar
     │
2009 ━━━━━━━━━━━━━━━━━━━━━━━━ CommonJS (Node.js)
     │                          • require() / module.exports
     │                          • Estándar del lado del servidor
     │                          • Carga síncrona
     │
2011 ━━━━━━━━━━━━━━━━━━━━━━━━ AMD (RequireJS)
     │                          • define() / require()
     │                          • Asíncrono para navegadores
     │                          • Sintaxis verbosa
     │
2015 ━━━━━━━━━━━━━━━━━━━━━━━━ Módulos ES6
     │                          • import / export
     │                          • Soporte nativo en navegadores
     │                          • Análisis estático
     │                          • EL ESTÁNDAR
     │
2020+ ━━━━━━━━━━━━━━━━━━━━━━━ Adopción Universal
                               • Todos los navegadores modernos soportan
                               • Node.js soporta ESM
                               • Build tools optimizan
                               • El futuro es modular

🎓 Teoría: Entendiendo la Sintaxis de Módulos ES6

Exportaciones Nombradas

Exportar múltiples valores con sus nombres:

// components/Button.js
export function PrimaryButton(text) {
  return `<button class="btn-primary">${text}</button>`;
}

export function SecondaryButton(text) {
  return `<button class="btn-secondary">${text}</button>`;
}

export const buttonStyles = {
  primary: 'bg-blue-500',
  secondary: 'bg-gray-500'
};

Importar exportaciones nombradas:

// Opción 1: Importar elementos específicos
import { PrimaryButton, SecondaryButton } from './components/Button.js';

// Opción 2: Importar todo como namespace
import * as Buttons from './components/Button.js';
Buttons.PrimaryButton('Haz clic');

// Opción 3: Renombrar imports
import { PrimaryButton as PBtn } from './components/Button.js';

Exportaciones Por Defecto

Exportar un único valor “principal”:

// components/Card.js
export default function Card({ title, content }) {
  return `
    <article class="card">
      <h3>${title}</h3>
      <p>${content}</p>
    </article>
  `;
}

Importar exportación por defecto:

// Tú eliges el nombre
import Card from './components/Card.js';
import MiCard from './components/Card.js'; // ¡Lo mismo!

Mezclar Exportaciones Por Defecto y Nombradas

// utils.js
export default function mainUtility() {
  return 'Función principal';
}

export function helperOne() {
  return 'Ayudante 1';
}

export function helperTwo() {
  return 'Ayudante 2';
}

Importar ambas:

import mainUtility, { helperOne, helperTwo } from './utils.js';
// Por defecto primero, luego nombradas entre llaves

Re-exportación (Patrón Barrel)

Crear un index.js para agregar exportaciones:

// components/index.js
export { PrimaryButton, SecondaryButton } from './Button.js';
export { default as Card } from './Card.js';
export { Section } from './Section.js';

// Ahora importa todo desde un lugar:
// import { PrimaryButton, Card, Section } from './components/index.js';

“El patrón barrel no es solo conveniencia—es arquitectura. Crea una fachada que oculta detalles de implementación.” — Martin Fowler, Refactoring (inspirado)

Importaciones Dinámicas

Cargar módulos condicionalmente o bajo demanda:

// Import estático (carga inmediata)
import utils from './utils.js';

// Import dinámico (carga cuando se necesita)
async function loadFeature() {
  const module = await import('./heavy-feature.js');
  module.initialize();
}

// Caso de uso: Code splitting
button.addEventListener('click', async () => {
  const { animate } = await import('./animations.js');
  animate(element);
});

🏗️ Metodología Atelier: Taller Práctico de Módulos

🛠️ Configuración: Creando un Proyecto Modular

Paso 1: Estructura del Proyecto

mkdir js-modules-workshop
cd js-modules-workshop

# Crear estructura
mkdir -p src/{components,utils,views}
touch index.html src/main.js
js-modules-workshop/
├── index.html
└── src/
    ├── main.js           # Punto de entrada
    ├── components/       # Componentes UI reutilizables
    │   ├── Button.js
    │   ├── Card.js
    │   └── index.js      # Archivo barrel
    ├── utils/            # Funciones auxiliares
    │   ├── math.js
    │   └── dom.js
    └── views/            # Vistas de página
        ├── home.js
        └── about.js

Paso 2: HTML con Soporte de Módulos

<!-- index.html -->
<!DOCTYPE html>
<html lang="es">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Taller de Módulos JS</title>
  <style>
    body {
      font-family: system-ui, sans-serif;
      max-width: 800px;
      margin: 2rem auto;
      padding: 0 1rem;
    }
    .btn {
      padding: 0.5rem 1rem;
      margin: 0.5rem;
      border: none;
      border-radius: 0.25rem;
      cursor: pointer;
    }
    .btn-primary { background: #3b82f6; color: white; }
    .btn-secondary { background: #6b7280; color: white; }
    .card {
      border: 1px solid #e5e7eb;
      border-radius: 0.5rem;
      padding: 1.5rem;
      margin: 1rem 0;
    }
  </style>
</head>
<body>
  <h1>Taller de Módulos JavaScript</h1>
  <div id="app"></div>
  
  <!-- ⚠️ Importante: type="module" habilita módulos ES6 -->
  <script type="module" src="src/main.js"></script>
</body>
</html>

📦 Ejercicio 1: Crear Módulos de Utilidades

src/utils/math.js - Exportaciones nombradas

/**
 * Utilidades matemáticas
 * Demuestra múltiples exportaciones nombradas
 */

export function add(a, b) {
  return a + b;
}

export function subtract(a, b) {
  return a - b;
}

export function multiply(a, b) {
  return a * b;
}

export function divide(a, b) {
  if (b === 0) throw new Error('División por cero');
  return a / b;
}

// Exportar constante
export const PI = 3.14159;
export const E = 2.71828;

// Exportar objeto
export const MathConstants = {
  PI,
  E,
  GOLDEN_RATIO: 1.61803
};

src/utils/dom.js - Exportaciones por defecto + nombradas

/**
 * Utilidades DOM
 * Demuestra exportaciones por defecto y nombradas juntas
 */

// Exportación por defecto - utilidad principal
export default function createEl(tag, attrs = {}, children = []) {
  const el = document.createElement(tag);
  
  // Establecer atributos
  Object.entries(attrs).forEach(([key, value]) => {
    if (key === 'className') {
      el.className = value;
    } else if (key.startsWith('on')) {
      // Event listener
      el.addEventListener(key.substring(2).toLowerCase(), value);
    } else {
      el.setAttribute(key, value);
    }
  });
  
  // Añadir hijos
  children.forEach(child => {
    if (typeof child === 'string') {
      el.appendChild(document.createTextNode(child));
    } else {
      el.appendChild(child);
    }
  });
  
  return el;
}

// Exportaciones nombradas - funciones auxiliares
export function append(parent, ...children) {
  children.forEach(child => parent.appendChild(child));
}

export function clear(element) {
  element.innerHTML = '';
}

export function setText(element, text) {
  element.textContent = text;
}

export function addClass(element, ...classes) {
  element.classList.add(...classes);
}

export function removeClass(element, ...classes) {
  element.classList.remove(...classes);
}

🎨 Ejercicio 2: Crear Módulos de Componentes

src/components/Button.js

/**
 * Componente Button
 * Demuestra patrones de componentes con módulos
 */

// Exportaciones nombradas para variantes de botón
export function PrimaryButton(text, onClick) {
  const button = document.createElement('button');
  button.className = 'btn btn-primary';
  button.textContent = text;
  if (onClick) button.addEventListener('click', onClick);
  return button;
}

export function SecondaryButton(text, onClick) {
  const button = document.createElement('button');
  button.className = 'btn btn-secondary';
  button.textContent = text;
  if (onClick) button.addEventListener('click', onClick);
  return button;
}

// Exportación de objeto de configuración
export const ButtonConfig = {
  defaultClass: 'btn',
  variants: ['primary', 'secondary'],
  sizes: ['sm', 'md', 'lg']
};

// Factory genérico de botones (exportación por defecto)
export default function Button({ 
  text = 'Botón', 
  variant = 'primary', 
  onClick = null 
} = {}) {
  const button = document.createElement('button');
  button.className = `btn btn-${variant}`;
  button.textContent = text;
  if (onClick) button.addEventListener('click', onClick);
  return button;
}

src/components/Card.js

/**
 * Componente Card
 * Demuestra patrón de exportación por defecto
 */

export default function Card({ title, content, footer = null } = {}) {
  const card = document.createElement('article');
  card.className = 'card';
  
  if (title) {
    const h3 = document.createElement('h3');
    h3.textContent = title;
    card.appendChild(h3);
  }
  
  if (content) {
    const p = document.createElement('p');
    p.textContent = content;
    card.appendChild(p);
  }
  
  if (footer) {
    const div = document.createElement('div');
    div.className = 'card-footer';
    div.appendChild(footer);
    card.appendChild(div);
  }
  
  return card;
}

// Exportación nombrada para utilidades de card
export function CardGrid(cards = []) {
  const grid = document.createElement('div');
  grid.className = 'card-grid';
  cards.forEach(card => grid.appendChild(card));
  return grid;
}

src/components/index.js - Archivo barrel

/**
 * Archivo barrel de componentes
 * Agrega todas las exportaciones de componentes
 * 
 * Este es el "patrón fachada" - proporciona una API pública limpia
 * mientras oculta detalles de implementación internos
 */

// Re-exportar todo desde Button
export { 
  PrimaryButton, 
  SecondaryButton, 
  ButtonConfig,
  default as Button 
} from './Button.js';

// Re-exportar desde Card
export { default as Card, CardGrid } from './Card.js';

// Ahora los consumidores pueden hacer:
// import { Button, Card, PrimaryButton } from './components/index.js';

🎬 Ejercicio 3: Crear Módulos de Vistas

src/views/home.js

/**
 * Vista Home
 * Demuestra importación desde múltiples módulos
 */

import { PrimaryButton, SecondaryButton, Card } from '../components/index.js';
import { add, multiply, MathConstants } from '../utils/math.js';
import createEl, { append, clear } from '../utils/dom.js';

export default function HomeView(container) {
  clear(container);
  
  // Crear header
  const header = createEl('header', {}, [
    createEl('h2', {}, ['Bienvenido a JavaScript Modular'])
  ]);
  
  // Crear card con demo de matemáticas
  const mathCard = Card({
    title: 'Demo de Utilidades Matemáticas',
    content: `
      2 + 3 = ${add(2, 3)}
      4 × 5 = ${multiply(4, 5)}
      π ≈ ${MathConstants.PI}
    `,
    footer: SecondaryButton('Calcular Más', () => {
      alert(`Razón Áurea: ${MathConstants.GOLDEN_RATIO}`);
    })
  });
  
  // Crear sección de botones
  const buttonsSection = createEl('section', {}, [
    createEl('h3', {}, ['Biblioteca de Componentes']),
    PrimaryButton('Acción Primaria', () => alert('¡Primario clickeado!')),
    SecondaryButton('Acción Secundaria', () => alert('¡Secundario clickeado!'))
  ]);
  
  // Añadir todo al contenedor
  append(container, header, mathCard, buttonsSection);
}

src/views/about.js

/**
 * Vista About
 * Demuestra imports dinámicos
 */

import { Card, CardGrid } from '../components/index.js';
import createEl, { append, clear } from '../utils/dom.js';

export default async function AboutView(container) {
  clear(container);
  
  const header = createEl('h2', {}, ['Sobre Arquitectura Modular']);
  
  const cards = [
    Card({
      title: 'Encapsulación',
      content: 'Cada módulo tiene su propio scope, previniendo contaminación global.'
    }),
    Card({
      title: 'Reutilización',
      content: 'Escribe una vez, importa en cualquier lugar. Principio DRY en acción.'
    }),
    Card({
      title: 'Mantenibilidad',
      content: 'Los cambios están localizados. Arregla una vez, beneficia en todas partes.'
    })
  ];
  
  const grid = CardGrid(cards);
  
  // Demo de import dinámico
  const dynamicButton = createEl('button', {
    className: 'btn btn-primary',
    onClick: async () => {
      // Lazy load del módulo math solo cuando se necesita
      const math = await import('../utils/math.js');
      alert(`PI cargado dinámicamente: ${math.PI}`);
    }
  }, ['Cargar Math Dinámicamente']);
  
  append(container, header, grid, dynamicButton);
}

🚀 Ejercicio 4: Punto de Entrada Principal

src/main.js - Entrada de la aplicación

/**
 * Punto de entrada principal
 * Demuestra orquestación de módulos
 */

import HomeView from './views/home.js';
import AboutView from './views/about.js';
import { PrimaryButton, SecondaryButton } from './components/index.js';

// Obtener contenedor de app
const app = document.getElementById('app');

// Router simple
const routes = {
  home: HomeView,
  about: AboutView
};

let currentRoute = 'home';

// Navegación
function navigate(route) {
  if (routes[route]) {
    currentRoute = route;
    routes[route](app);
  }
}

// Crear navegación
function createNav() {
  const nav = document.createElement('nav');
  nav.style.marginBottom = '2rem';
  
  nav.appendChild(
    PrimaryButton('Inicio', () => navigate('home'))
  );
  nav.appendChild(
    SecondaryButton('Acerca', () => navigate('about'))
  );
  
  document.body.insertBefore(nav, app);
}

// Inicializar app
function init() {
  console.log('🚀 App JavaScript Modular Inicializada');
  console.log('📦 Todos los módulos cargados via imports ES6');
  
  createNav();
  navigate('home');
}

// Iniciar
init();

// Hacer navigate disponible globalmente para debugging
window.navigate = navigate;

🧪 Probando Tu Sistema de Módulos

Método 1: Servidor de Desarrollo Local

# Opción A: Python
python3 -m http.server 8000

# Opción B: Node.js (si tienes http-server)
npx http-server -p 8000

# Opción C: PHP
php -S localhost:8000

Luego abre: http://localhost:8000

⚠️ Importante: Los módulos ES6 requieren un servidor debido a CORS. No puedes usar el protocolo file://.

Método 2: Usar Vite (Recomendado)

npm create vite@latest js-modules-workshop -- --template vanilla
cd js-modules-workshop
npm install
npm run dev

🎯 Ejercicios Prácticos: Construye Tus Habilidades Modulares

Ejercicio 5.1: Crear un Componente de Notificación

Requisitos:

  1. Crear src/components/Notification.js
  2. Exportación por defecto: Notification({ message, type })
  3. Exportaciones nombradas: SuccessNotification(), ErrorNotification(), WarningNotification()
  4. Usar el componente en HomeView

Código inicial:

// src/components/Notification.js
export default function Notification({ message, type = 'info' } = {}) {
  // TODO: Crear elemento de notificación
  // Tipos: 'success', 'error', 'warning', 'info'
}

export function SuccessNotification(message) {
  return Notification({ message, type: 'success' });
}

// TODO: Añadir ErrorNotification y WarningNotification

Ejercicio 5.2: Crear un Módulo de Formulario

Requisitos:

  1. Crear src/components/Form.js
  2. Exportar FormField(), FormButton(), Form()
  3. Crear src/views/contact.js que use estos componentes
  4. Añadir ruta a main.js

Ejercicio 5.3: Construir un Módulo de Gestión de Estado

Requisitos:

  1. Crear src/utils/store.js
  2. Implementar gestión simple de estado:
// src/utils/store.js
const state = {};
const listeners = [];

export function setState(key, value) {
  state[key] = value;
  listeners.forEach(fn => fn(state));
}

export function getState(key) {
  return state[key];
}

export function subscribe(callback) {
  listeners.push(callback);
  // Retornar función de desuscripción
  return () => {
    const index = listeners.indexOf(callback);
    if (index > -1) listeners.splice(index, 1);
  };
}

Usarlo en tu app:

// src/main.js
import { setState, getState, subscribe } from './utils/store.js';

subscribe(state => {
  console.log('Estado cambió:', state);
});

setState('user', { name: 'Alice', role: 'developer' });
console.log(getState('user'));

🏆 Mejores Prácticas: El Manifiesto Modular

1. Una Responsabilidad Por Módulo

“Un módulo debe hacer una cosa y hacerla bien.” — Filosofía Unix aplicada a JavaScript

// ✅ Bueno: Responsabilidad enfocada
// src/utils/validation.js
export function isEmail(str) { /* ... */ }
export function isURL(str) { /* ... */ }
export function isPhone(str) { /* ... */ }

// ❌ Malo: Responsabilidades mezcladas
// src/utils/everything.js
export function isEmail(str) { /* ... */ }
export function fetchAPI(url) { /* ... */ } // ¡Diferente responsabilidad!
export function formatCurrency(num) { /* ... */ } // ¡Diferente responsabilidad!

2. Dependencias Explícitas

// ✅ Bueno: Todas las dependencias visibles arriba
import { Button } from './components/Button.js';
import { Card } from './components/Card.js';
import { add, multiply } from './utils/math.js';

function myFeature() {
  // Usar imports
}

// ❌ Malo: Dependencias ocultas (imports dinámicos por todas partes)
function myFeature() {
  const math = await import('./utils/math.js'); // Difícil de rastrear
}

3. Preferir Exportaciones Nombradas para Utilidades

// ✅ Bueno: Exportaciones nombradas para múltiples utilidades
// utils/array.js
export function unique(arr) { /* ... */ }
export function flatten(arr) { /* ... */ }
export function chunk(arr, size) { /* ... */ }

// Importar lo que necesites
import { unique, flatten } from './utils/array.js';

4. Preferir Exportaciones Por Defecto para Módulos de Propósito Único

// ✅ Bueno: Exportación por defecto para componente único
// components/Hero.js
export default function Hero({ title, subtitle }) {
  // Componente principal único
}

// Importar con nombre semántico
import Hero from './components/Hero.js';

5. Usar Archivos Barrel para APIs Públicas

// ✅ Bueno: API pública limpia
// components/index.js
export { Button, PrimaryButton } from './Button.js';
export { Card } from './Card.js';
export { Hero } from './Hero.js';

// Los consumidores ven API limpia
import { Button, Card, Hero } from './components/index.js';

// ❌ Malo: Exponer estructura interna
import Button from './components/internal/ui/Button.js';

6. Evitar Dependencias Circulares

// ❌ Malo: Dependencia circular
// A.js
import { functionB } from './B.js';
export function functionA() { functionB(); }

// B.js
import { functionA } from './A.js'; // ⚠️ ¡Circular!
export function functionB() { functionA(); }

// ✅ Bueno: Extraer lógica compartida
// shared.js
export function sharedLogic() { /* ... */ }

// A.js
import { sharedLogic } from './shared.js';

// B.js
import { sharedLogic } from './shared.js';

7. Documentar Tus Módulos con JSDoc

/**
 * Componente Card
 * 
 * Crea un elemento card estilizado para mostrar contenido
 * 
 * @module components/Card
 * @param {Object} options - Configuración del card
 * @param {string} options.title - Título del card
 * @param {string} options.content - Contenido del card
 * @param {HTMLElement} [options.footer] - Elemento footer opcional
 * @returns {HTMLElement} El elemento card
 * 
 * @example
 * const card = Card({
 *   title: 'Hola',
 *   content: 'Mundo'
 * });
 */
export default function Card({ title, content, footer = null } = {}) {
  // Implementación
}

🌍 Impacto en el Mundo Real: Por Qué Esto Importa

“La diferencia entre un principiante y un profesional no es lo que saben, sino cómo organizan lo que saben.” — Anónimo

Antes de los Módulos: La Pesadilla del Mantenimiento

// app.js - 5000 líneas de código inmantenible
var app = {
  // 100+ funciones todas en un archivo
  // Variables globales por doquier
  // Dependencias poco claras
  // ¿Testing? Buena suerte.
};

Después de los Módulos: Arquitectura Escalable

src/
├── components/        # 50+ componentes reutilizables
├── utils/             # Utilidades probadas y documentadas
├── views/             # Estructura clara de páginas
├── services/          # Interacciones con API
└── main.js            # Orquestación limpia

Beneficios Realizados:

  • Colaboración en Equipo: Diferentes devs trabajan en diferentes módulos
  • Reutilización de Código: Componente Button usado en 20 lugares, mantenido en 1
  • Testing: Prueba unitaria de cada módulo independientemente
  • Rendimiento: Tree-shaking elimina código no usado
  • Onboarding: Nuevos desarrolladores entienden la estructura rápidamente

📚 Lecturas Adicionales y Referencias

Artículos Esenciales

Libros (Citas Inspiradas)

  • Kyle Simpson, You Don’t Know JS: ES6 & Beyond
  • Axel Rauschmayer, Exploring ES6
  • Douglas Crockford, JavaScript: The Good Parts
  • Martin Fowler, Refactoring: Improving the Design of Existing Code

Contexto Histórico

  • Node.js (2009): Trajo prominencia a CommonJS
  • RequireJS (2010): AMD para navegadores
  • Browserify (2011): CommonJS en navegadores
  • Webpack (2012): Bundler de módulos universal
  • ES6 (2015): Estándar nativo de módulos
  • Vite (2020): Build tool ultrarrápido ESM-first

🎓 Reflexión Atelier: Lo Que Has Aprendido

Al completar esta lección, has dominado:

  1. Evolución Histórica - Del caos global a módulos ES6
  2. Sintaxis de Módulos - import, export, default, nombrados, dinámicos
  3. Patrones Arquitectónicos - IIFE, CommonJS, AMD, ES6
  4. Mejores Prácticas - SRP, dependencias explícitas, archivos barrel
  5. Habilidades Prácticas - Construiste una aplicación modular completa
  6. Mentalidad Profesional - Entender trade-offs y evolución

“Ahora estás equipado para construir aplicaciones web escalables y mantenibles. Usa este poder sabiamente—organiza tu código como si la próxima persona en mantenerlo fuera un maníaco homicida que sabe dónde vives.” — Anónimo (pero sabio) Desarrollador


🚀 Próximos Pasos

  1. Refactoriza un proyecto existente para usar módulos ES6
  2. Construye una biblioteca de componentes con estructura modular adecuada
  3. Estudia proyectos open-source para ver módulos en acción (React, Vue, etc.)
  4. Explora build tools (Vite, Webpack, Rollup) para optimización de módulos
  5. Aplica a lecciones de Tailwind - Revisa S2-S5 con nuevo entendimiento

Recuerda: Pensar de forma modular es pensar de forma profesional. Cada archivo que creas es un compromiso con la mantenibilidad futura.


📝 Commit Tu Aprendizaje

git add .
git commit -m "feat: Completar taller de Módulos JS

- Entender evolución histórica desde IIFE hasta ES6
- Dominar sintaxis import/export (default, nombrados, dinámicos)
- Construir aplicación modular con components/utils/views
- Aplicar mejores prácticas: SRP, archivos barrel, JSDoc
- Listo para construir aplicaciones web escalables

Progreso de aprendizaje: Principiante → Mentalidad de arquitectura profesional"

🎨 Filosofía Atelier: “No enseñamos código—enseñamos a pensar. Los módulos no son sintaxis, son arquitectura. La arquitectura no es estructura, es cultura. La cultura es cómo trabajamos juntos a través del tiempo y el espacio. Bienvenido a la comunidad de maestros web vernáculos.”

— Prof. Rubén Vega Balbás, PhD