CommonJS 与 ES Module 的区别

在 JavaScript 开发中,模块化系统扮演着重要角色,它帮助我们管理和组织代码。CommonJS 和 ES Module 是两种主要的模块化标准,它们在模块的加载、导出机制和兼容性等方面存在显著区别。本文将详细比较这两种模块系统的主要特点和区别。

1. 模块加载方式

CommonJS:

  • 同步加载:CommonJS 采用同步加载模块的方式。这意味着当使用 require() 函数时,模块会立即加载和执行。这种方式在服务器端(如 Node.js)非常合适,但在浏览器环境中可能导致性能问题,因为浏览器需要在模块加载完成后才能继续执行后续代码。

    1
    // 导入模块
    2
    const math = require("./math");
    3
    console.log(math.add(2, 3));

ES Module:

  • 静态和异步加载:ES Module 支持静态和异步加载。模块的导入在编译阶段静态分析,这允许 JavaScript 引擎进行优化。ES Module 支持通过 import 语法进行异步加载(使用动态 import()),这在浏览器环境中尤为重要。

    1
    // 静态导入
    2
    import { add } from "./math.js";
    3
    console.log(add(2, 3));
    4
    5
    // 动态导入
    6
    import("./math.js").then((module) => {
    7
    console.log(module.add(2, 3));
    8
    });

2. 模块导出和导入

CommonJS:

  • 模块导出:使用 module.exportsexports 对象来导出模块的功能。可以导出对象、函数或其他值。

    1
    // 导出模块
    2
    module.exports = {
    3
    add: function (a, b) {
    4
    return a + b;
    5
    },
    6
    };
  • 模块导入:使用 require() 函数来导入模块。

    1
    // 导入模块
    2
    const math = require("./math");
    3
    console.log(math.add(2, 3));

ES Module:

  • 模块导出:使用 export 关键字来导出模块的功能,支持命名导出和默认导出。

    1
    // 命名导出
    2
    export function add(a, b) {
    3
    return a + b;
    4
    }
    5
    6
    // 默认导出
    7
    export default function add(a, b) {
    8
    return a + b;
    9
    }
  • 模块导入:使用 import 关键字来导入模块,支持导入模块的一部分或整个模块。

    1
    // 导入命名导出
    2
    import { add } from "./math.js";
    3
    console.log(add(2, 3));
    4
    5
    // 导入默认导出
    6
    import add from "./math.js";
    7
    console.log(add(2, 3));

3. 模块解析

CommonJS:

  • 动态解析:模块路径是动态解析的,可以在代码运行时计算模块路径。这使得条件导入或动态加载模块变得可能。

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

ES Module:

  • 静态解析:模块路径在编译阶段静态解析,编译器可以在代码运行之前确定依赖关系。这使得静态分析和优化变得可能,但不支持动态解析路径。

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

4. 兼容性

CommonJS:

  • 主要用于 Node.js:CommonJS 是 Node.js 的默认模块系统,浏览器环境不直接支持 CommonJS 模块,但可以通过工具如 Browserify 或 Webpack 来使用 CommonJS 模块。

ES Module:

  • 标准化:ES Module 是 ECMAScript 标准的一部分,现代浏览器和 Node.js 都广泛支持。它是前端和后端都推荐的模块系统。

5. 运行时行为

CommonJS:

  • 动态加载和缓存:CommonJS 模块在第一次 require 时加载和执行,随后缓存。对同一个模块的后续 require 调用会返回缓存中的模块实例。

ES Module:

  • 静态加载和环形依赖:ES Module 支持静态分析,使得模块的依赖关系可以在编译阶段确定。对于环形依赖,ES Module 允许部分加载,这意味着模块在加载时仍然可以引用其他模块的部分内容。

6. 导出值 vs. 导出引用

CommonJS:

  • 导出值是拷贝:CommonJS 中,模块导出的值是导出对象的一个拷贝。修改导出的对象不会影响其他模块中看到的值。

    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()); // 输出: 0
    15
    math.count = 10; // 不会改变 math.getCount() 返回值
    16
    console.log(math.getCount()); // 仍然输出: 0

ES Module:

  • 导出值是引用:ES Module 中,模块导出的值是对原始对象的引用。对导出对象的修改会在其他模块中反映出来。

    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); // 输出: 0
    10
    count = 10; // 会改变 math.js 中的 count
    11
    import { count as newCount } from "./math.js";
    12
    console.log(newCount); // 仍然输出: 10

结论

  • CommonJS 适合服务器端(Node.js)使用,其模块以同步方式加载,简单易用,但在浏览器环境中需要额外的工具支持。
  • ES Module 是 ECMAScript 的标准模块系统,支持静态分析和优化,适合现代浏览器和 Node.js,具有更好的性能和灵活性。

选择适合的模块系统可以帮助提高代码的可维护性和性能,根据项目需求和环境进行选择是至关重要的。

美团外卖红包 饿了么红包 支付宝红包