Diferencias entre CommonJS y ES Module

En el desarrollo de JavaScript, los sistemas de módulos juegan un papel crucial al ayudarnos a gestionar y organizar el código. CommonJS y ES Module son dos estándares principales de modularización que presentan diferencias significativas en la carga de módulos, el mecanismo de exportación y la compatibilidad. Este artículo comparará en detalle las principales características y diferencias entre estos dos sistemas de módulos.

1. Forma de carga de módulos

CommonJS:

  • Carga sincrónica: CommonJS utiliza un método de carga sincrónica para los módulos. Esto significa que al usar la función require(), el módulo se carga y ejecuta inmediatamente. Este método es muy adecuado para el lado del servidor (como Node.js), pero puede causar problemas de rendimiento en entornos de navegador, ya que el navegador necesita completar la carga del módulo antes de continuar con el código siguiente.

    1
    // Importar módulo
    2
    const math = require("./math");
    3
    console.log(math.add(2, 3));

ES Module:

  • Carga estática y asíncrona: ES Module admite carga estática y asíncrona. La importación de módulos se analiza de forma estática en la fase de compilación, lo que permite al motor de JavaScript optimizar. ES Module admite la carga asíncrona a través de la sintaxis import (utilizando import() dinámico), lo que es particularmente importante en entornos de navegador.

    1
    // Importación estática
    2
    import { add } from "./math.js";
    3
    console.log(add(2, 3));
    4
    5
    // Importación dinámica
    6
    import("./math.js").then((module) => {
    7
    console.log(module.add(2, 3));
    8
    });

2. Exportación e importación de módulos

CommonJS:

  • Exportación de módulos: Se usa module.exports o el objeto exports para exportar la funcionalidad del módulo. Se pueden exportar objetos, funciones u otros valores.

    1
    // Exportar módulo
    2
    module.exports = {
    3
    add: function (a, b) {
    4
    return a + b;
    5
    },
    6
    };
  • Importación de módulos: Se usa la función require() para importar módulos.

    1
    // Importar módulo
    2
    const math = require("./math");
    3
    console.log(math.add(2, 3));

ES Module:

  • Exportación de módulos: Se usa la palabra clave export para exportar la funcionalidad del módulo, soportando exportaciones nombradas y exportaciones por defecto.

    1
    // Exportación nombrada
    2
    export function add(a, b) {
    3
    return a + b;
    4
    }
    5
    6
    // Exportación por defecto
    7
    export default function add(a, b) {
    8
    return a + b;
    9
    }
  • Importación de módulos: Se usa la palabra clave import para importar módulos, soportando la importación de parte del módulo o del módulo completo.

    1
    // Importación de exportaciones nombradas
    2
    import { add } from "./math.js";
    3
    console.log(add(2, 3));
    4
    5
    // Importación de exportación por defecto
    6
    import add from "./math.js";
    7
    console.log(add(2, 3));

3. Resolución de módulos

CommonJS:

  • Resolución dinámica: Las rutas de los módulos se resuelven dinámicamente, lo que permite calcular las rutas de los módulos en tiempo de ejecución. Esto hace posible la importación condicional o la carga dinámica de módulos.

    1
    const math = require("./math-" + someCondition + ".js");

ES Module:

  • Resolución estática: Las rutas de los módulos se resuelven estáticamente en la fase de compilación, lo que permite al compilador determinar las dependencias antes de que el código se ejecute. Esto permite el análisis estático y la optimización, pero no soporta la resolución dinámica de rutas.

    1
    import { add } from "./math.js";

4. Compatibilidad

CommonJS:

  • Principalmente para Node.js: CommonJS es el sistema de módulos por defecto en Node.js. Los entornos de navegador no soportan directamente los módulos CommonJS, pero se pueden usar herramientas como Browserify o Webpack para trabajar con módulos CommonJS.

ES Module:

  • Estandarización: ES Module es parte del estándar ECMAScript, y es ampliamente soportado tanto en navegadores modernos como en Node.js. Es el sistema de módulos recomendado tanto para el frontend como para el backend.

5. Comportamiento en tiempo de ejecución

CommonJS:

  • Carga dinámica y caché: Los módulos CommonJS se cargan y ejecutan en la primera llamada a require, y luego se almacenan en caché. Las llamadas subsecuentes a require para el mismo módulo devolverán la instancia del módulo en caché.

ES Module:

  • Carga estática y dependencias circulares: ES Module soporta el análisis estático, permitiendo que las dependencias de los módulos se determinen en la fase de compilación. Para las dependencias circulares, ES Module permite la carga parcial, lo que significa que un módulo aún puede referenciar partes de otros módulos mientras se carga.

6. Valores exportados vs. Referencias exportadas

CommonJS:

  • Valores exportados son copias: En CommonJS, los valores exportados del módulo son una copia del objeto exportado. Modificar el objeto exportado no afectará los valores vistos en otros módulos.

    math.js
    1
    let count = 0;
    2
    3
    module.exports = {
    4
    add: function (a, b) {
    5
    return a + b;
    6
    },
    7
    getCount: function () {
    8
    return count;
    9
    },
    10
    };
    11
    12
    // app.js
    13
    const math = require("./math");
    14
    console.log(math.getCount()); // Salida: 0
    15
    math.count = 10; // No cambiará el valor devuelto por math.getCount()
    16
    console.log(math.getCount()); // Aún muestra: 0

ES Module:

  • Valores exportados son referencias: En ES Module, los valores exportados del módulo son una referencia al objeto original. Las modificaciones al objeto exportado se reflejarán en otros módulos.

    math.js
    1
    export let count = 0;
    2
    3
    export function add(a, b) {
    4
    return a + b;
    5
    }
    6
    7
    // app.js
    8
    import { count, add } from "./math.js";
    9
    console.log(count); // Salida: 0
    10
    count = 10; // Cambia el count en math.js
    11
    import { count as newCount } from "./math.js";
    12
    console.log(newCount); // Aún muestra: 10

Conclusión

  • CommonJS es adecuado para el lado del servidor (Node.js), sus módulos se cargan de manera sincrónica, son simples de usar, pero en un entorno de navegador requieren herramientas adicionales para su soporte.
  • ES Module es el sistema de módulos estándar de ECMAScript, admite análisis estático y optimización, es adecuado para navegadores modernos y Node.js, ofreciendo mejor rendimiento y flexibilidad.

Elegir el sistema de módulos adecuado puede ayudar a mejorar la mantenibilidad y el rendimiento del código, por lo que es crucial elegir según las necesidades y el entorno del proyecto.