1.3 · Módulos: CommonJS (require) frente a ES Modules

Nivel 1 · JavaScript y bases de Node

1.3Módulos: CommonJS (require) frente a ES Modules

Objetivo: dividir su código en módulos y dominar los dos sistemas (CommonJS require/module.exports y ESM import/export), entendiendo el principio común.
Tiempo estimado: 11 min · lectura autónoma · Si solo tienes 5 min: lee «Lo esencial en 30s» + el esquema.

Lo esencial en 30 segundos

  • Un módulo = un archivo, una responsabilidad: se exporta lo que se comparte, se importa lo que se necesita — base de la organización.
  • CommonJS (histórico, omnipresente): module.exports para exportar, require() para importar — síncrono, sencillo, en todas partes en npm.
  • ES Modules (estándar, futuro): export/import, activados vía .mjs o "type": "module" — recomendado para un proyecto nuevo, pero hay que leer los dos.

Minicaso hilado — Salim divide su aplicación en Argel

La aplicación de Salim cabe en un solo archivo de 500 líneas: las funciones de cálculo mezcladas con las lecturas de archivos, imposible de probar. La divide en tres módulos — cálculos, lectura de datos, visualización — cada uno exportando sus funciones: primero en CommonJS (module.exports/require) para entender, luego activa los ES Modules ("type": "module") para su nuevo proyecto. Cada módulo tiene una responsabilidad clara y se prueba por separado.

Continuación del caso 1.1-1.2 — Salim estructura su código tras el lenguaje y la asincronía.

1. CommonJS: module.exports y require()

Ningún programa serio cabe en un solo archivo: se divide en módulos (archivos con una responsabilidad, que se importan). El sistema histórico de Node es CommonJS: se exporta con module.exports (module.exports = { fn1, fn2 }) y se importa con require() (const truc = require('./monModule')). Síncrono, sencillo, presente en millones de paquetes npm. Un módulo local empieza por ./ o ../; un módulo nativo o npm se importa por su nombre (require('fs')).

Salim crea calculs.js con module.exports = { additionner, soustraire } y lo importa con require('./calculs'): su archivo de 500 líneas empieza a respirar.

2. ES Modules: export e import

Los ES Modules son la sintaxis estándar del lenguaje (la misma que en el navegador moderno): se exporta con export (export function fn() {}) y se importa con import (import { fn } from './autre.js'). Se activan nombrando sus archivos .mjs o — más habitual — añadiendo "type": "module" en el package.json. ESM está recomendado para un proyecto nuevo (estándar, futuro, coherente con el front-end).

Salim añade "type": "module" a su nuevo proyecto y pasa a import { additionner } from './calculs.js' — observando la extensión .js obligatoria en la ruta.

3. Entender los dos, dividir bien

CommonJS sigue omnipresente (código existente, tutoriales, paquetes): leerá muchísimo require/module.exports incluso escribiendo import/export. Mezclar ambos requiere precauciones (importar CommonJS desde ESM funciona, lo inverso es más delicado). Más allá de la sintaxis, lo esencial es el principio: dividir por responsabilidad, exportar una interfaz clara, nombrar los archivos explícitamente, evitar las dependencias circulares.

Salim nombra sus módulos calculs.js, donnees.js, affichage.js y solo exporta lo necesario: un desarrollador nuevo sabe dónde buscar sin preguntarle.

Error frecuente de interpretación: «require e import son intercambiables en el mismo archivo» o «basta con renombrar a .mjs sin cambiar nada». No. Cada archivo sigue UN sistema según la configuración (".type": "module" o no): mezclar las sintaxis sin explicarlo rompe la ejecución. Y pasar a ESM impone ajustes (extensiones explícitas en las rutas, __dirname ausente en ESM puro) — no es un simple renombrado.
Dos sistemas de módulos Mismo principio, sintaxis distinta CommonJS module.exports /require() — históricoES Modules export / import —estándar del lenguajePrincipio común Dividir,exportar, importar
Mismo principio, sintaxis distinta
Versión en texto del esquema (SEO / lectores de pantalla / traducción): 1) Dividir: Salim saca los cálculos, las lecturas y la visualización de su archivo único en tres módulos con responsabilidad clara. 2) Exportar/importar: en CommonJS vía module.exports/require('./calculs'), en ESM vía export/import con extensión explícita. 3) Elegir: ESM ("type": "module") para el proyecto nuevo, lectura habitual del CommonJS existente — sin mezclar ambas sintaxis en un mismo archivo. Lección: el principio (dividir, exportar, importar) prima sobre la sintaxis.

🔎 Ejemplo concreto — 🔎 Ejemplo concreto — Exportar e importar

Primero CommonJS, luego ES Modules: dos alternativas, un solo sistema por archivo.

// --- Alternativa A: CommonJS (archivo .js clásico) ---
// en calculs.js:
// module.exports = { additionner, soustraire };
// en app.js:
const calc = require("./calculs");
console.log(calc.additionner(2, 3));

Lectura: el módulo local se importa por su ruta en ./, el paquete o nativo por su nombre.

Lección: en ESM, el equivalente se escribe export function additionner + import { additionner } from './calculs.js' — con la extensión obligatoria.

Vocabulario de la sección

Módulo
Archivo separado con una responsabilidad, que exporta lo que pone a disposición e importa lo que necesita; base de la organización del código.

Ejemplo sencillo: Los calculs.js, donnees.js y affichage.js de Salim.

Lo que no es: un archivo único de 500 líneas que lo hace todo.

Reflejo: un archivo = una responsabilidad clara.

CommonJS (CJS)
Sistema histórico de módulos de Node: exportación con module.exports, importación con require(). Síncrono, sencillo, omnipresente en el ecosistema npm.

Ejemplo sencillo: El require('./calculs') de la primera división de Salim.

Lo que no es: la sintaxis import/export estándar del lenguaje.

Reflejo: leerlo con fluidez, incluso escribiendo ESM.

ES Modules (ESM)
Sistema estándar de módulos del lenguaje: exportación con export, importación con import. Activado vía .mjs o "type":"module" en package.json.

Ejemplo sencillo: El import { additionner } from './calculs.js' del nuevo proyecto de Salim.

Lo que no es: un require() en un archivo configurado en ESM.

Reflejo: proyecto nuevo = ESM, extensión explícita.

require frente a import
Las dos sintaxis de importación: require() (CommonJS) e import (ESM). Ambas coexisten en el código real, de ahí la necesidad de entender las dos.

Ejemplo sencillo: Salim lee require en los paquetes antiguos y escribe import en su proyecto.

Lo que no es: mezclarlos en un mismo archivo sin configuración adecuada.

Reflejo: un sistema por archivo, elegido con conocimiento.

Ruta de módulo
Local: empieza por ./ o ../ (archivos del proyecto). Nombre solo: módulo nativo (fs) o paquete npm de node_modules.

Ejemplo sencillo: El require('./calculs') local frente al require('fs') nativo en casa de Salim.

Lo que no es: require('calculs') sin ./ para un archivo del proyecto (no se encontrará).

Reflejo: ./ para el proyecto, nombre solo para el sistema y npm.

En la práctica — En la práctica — Organizar su código en módulos

  1. Cree un módulo que exporte funciones en CommonJS (module.exports = { fn1, fn2 }) e impórtelo con require('./monModule').
  2. Recree el mismo módulo en ESM (export function fn1() {}) e impórtelo con import (extensión .js explícita).
  3. Active ESM en un proyecto añadiendo "type": "module" al package.json, y observe que los .js se convierten en ES Modules.
  4. Importe un módulo nativo (fs) y un módulo local (por ./ruta) — detecte la diferencia de ruta.
Divide su código en módulos y domina los dos sistemas (CommonJS require/module.exports y ESM import/export), entendiendo el principio común de organización.

Guion listo para decir — proponer la división en módulos (30 s)

«Este archivo tiene 500 líneas y tres oficios: propongo que lo dividamos en tres módulos — cálculos, datos, visualización — cada uno con sus exports. Podremos probar cada parte por separado y reutilizarlas en otro lugar.»

Para decir en la revisión de código; mostrar el plan de los tres módulos antes de dividir.

Modelo copiar-pegar — módulo CommonJS + uso

// calculs.js: se exporta lo que se comparte
function additionner(a, b) {
  return a + b;
}
function soustraire(a, b) {
  return a - b;
}
module.exports = { additionner, soustraire };
// app.js: se importa lo que se necesita
const calc = require("./calculs");
console.log(calc.additionner(2, 3));
console.log(calc.soustraire(5, 2));

Archivo único que dividir

Detectar las responsabilidades (cálculos, datos, visualización), extraer un módulo a la vez verificando con node en cada etapa — como Salim con sus 500 líneas.

Proyecto nuevo en Argel

Salim arranca en ESM ("type": "module"): misma sintaxis import/export que el front-end del equipo, un solo sistema que conocer.

Paquetes antiguos en require

Ante documentación en require: saber leerla y usarla tal cual, sin convertir todo el proyecto por ello.

Nomenclatura de equipo

Un nombre de archivo que dice lo que hace (donnees.js, validation.js): la estructura se convierte en documentación gratuita para el próximo desarrollador.

✓ Buenas prácticas

  • Un módulo = una responsabilidad.
  • Exportar solo lo necesario.
  • Un sistema por archivo (sin mezclas).

✗ Errores frecuentes

  • Mezclar require e import sin config.
  • Olvidar ./ para un módulo local.
  • Olvidar la extensión en ESM.

Puntos clave

  • Dividir el código en MÓDULOS (archivos con una responsabilidad, que se exportan/importan) = fundamental para la organización y el mantenimiento.
  • COMMONJS (histórico, omnipresente): exportación module.exports, importación require(). Síncrono, en millones de paquetes npm.
  • ES MODULES (estándar, futuro): exportación export, importación import; activados vía .mjs o "type":"module" en package.json.
  • Entender AMBOS (require E import coexisten); ESM recomendado para un proyecto nuevo. El PRINCIPIO prima sobre la sintaxis.

Reto 48h — aplíquelo mañana

Extraiga un módulo de su código (modelo anterior): module.exports + require('./...'), ejecútelo con node y luego conviértalo en export/import. 20 minutos bastan.

Lista de salida de la sección
  • Puedo explicar módulo, export e import.
  • Sé distinguir require (CJS) de import (ESM).
  • He dividido un módulo y lo he importado de ambas formas.
Autoevaluación 1-5: ¿tienen mis archivos cada uno una responsabilidad clara?¿sé activar ESM y leer CommonJS?

Preguntas frecuentes

CommonJS o ES Modules: ¿cuál elegir para mi proyecto?

Para un proyecto nuevo, ESM está recomendado: es el estándar oficial del lenguaje (no un invento de Node), coherente con el front-end (misma sintaxis en el navegador) y la dirección de futuro. Pero CommonJS sigue siendo una opción válida (sencillez, compatibilidad con el código existente) — y sobre todo, leerá muchísimo CommonJS (paquetes, tutoriales, código existente). ¿Por qué dos sistemas? Node creó CommonJS en 2009 porque JavaScript no tenía entonces ningún sistema de módulos; el lenguaje estandarizó los ES Modules en 2015 y Node los adoptó sin romper lo existente. De ahí la coexistencia que hay que conocer.

¿Cómo organizar bien mi código en módulos, más allá de la sintaxis?

Es una competencia de arquitectura independiente de require/import. Cada módulo tiene una responsabilidad clara (usuarios, base de datos, rutas, utilidades) — nada de cajones de sastre. Divida por funcionalidad, exporte una interfaz clara y mínima (lo público, el resto queda privado), nombre explícitamente (userService.js, validation.js), evite las dependencias circulares (A importa B que importa A) organizando jerárquicamente. Como Salim: tres módulos nombrados valen más que un archivo de 500 líneas.

Comprueba tu comprensión

¿Cómo importar un módulo local en CommonJS?

Recursos adicionales (opcional, para ir más allá)

Examen del nivel 1Preguntas inéditas y más difíciles para validar tus conocimientos