对比ECMAScript 模块 (ESM) 和 CommonJS

更新日期: 2024-03-21阅读: 295标签: 模块

JavaScript 最初只是一种简单的语言,用于使静态网站更具活力和交互性。然而,用 JavaScript 编写的项目在很久以前就开始变得越来越复杂。正因为如此,我们很快就发现需要一种方法来将代码分解成更小、更易于管理的片段。多年来,人们对如何实现将 JavaScript 代码拆分成模块有很多不同的想法。在本文中,我们将比较最流行的几种方法:ECMAScript 模块 (ESM) 和 CommonJS。


CommonJS

Node.js 的创建者知道,代码需要组织成可重用的模块。然而,当 Node.js 于 2009 年首次推出时,JavaScript 还没有正式的模块系统。因此,Node.js 引入了 CommonJS 模块。


创建和导出模块

在 Node.js 中,每个文件都是一个独立的模块。要开始使用 CommonJS,让我们用一个简单的函数创建一个新文件。

sum.js

function sum(numberOne, numberTwo) {
return numberOne + numberTwo;
}

module.exports = { sum };

在每个文件中,module 变量都代表当前模块。我们可以使用 module.exports 来让其他模块导入和使用 sum 函数。


导入模块

要导入模块,我们需要使用 require 函数并提供正确的路径。

const { sum } = require('./sum');
console.log(sum(1, 2)); // 3

CommonJS 还支持 import 函数,它允许我们异步导入模块。

import('./sum.js')
.then(({ sum }) => {
console.log(sum(1, 2)); // 3
})


ECMAScript Modules

JavaScript 语言进行了一次重大升级,即 ES6 或 ECMAScript 2015。除其他功能外,它还包括模块管理的官方语法,即 ECMAScript Modules (ESM)。


创建和导出模块

我们必须使用导出关键字来公开模块中的各种值。

sum.js

function sum(numberOne, numberTwo) {
return numberOne + numberTwo;
}

export { sum };

使用上述语法,我们可以导出任意多个值。此外,模块还可以包含一个默认导出。

subtract.js:

function subtract(numberOne, numberTwo) {
return numberOne - numberTwo;
}

export default subtract;


导入和导出模块

要导入 ECMAScript 模块,我们必须使用 import 关键字。

import { sum } from './sum.js';
import subtract from './subtract.js';

console.log(sum(3, 2)); // 5
console.log(subtract(3, 2)); // 1

请注意,我们使用略有不同的语法来导入默认导出。重要的是,默认导入使用的名称不必与默认导出一致。

import sum from './subtract.js';
这也是有些人不喜欢使用默认出口的原因之一。

ESM 还支持导入功能,允许我们异步导入模块。

import('./sum.js')
.then(({ sum }) => {
console.log(sum(1, 2)); // 3
})


ECMAScript 模块的兼容

虽然 ECMAScript 模块是在 2015 年左右推出的,但社区还是花了一段时间才跟上步伐。不过,现在我们甚至可以在浏览器中原生使用 ESM。

webpack 等捆绑程序多年前就开始支持 ECMAScript 模块。它们可以将我们在多个 JavaScript 文件中使用 ESM 的代码转换成单个文件输出。它还可以将捆绑包拆分成多个文件,以提高性能。

Node.js 在 8.5.0 版左右开始尝试支持 ESM。不过当时要使用它,我们必须包含 --experimental-modules 模块。在 13.2.0 版中,他们取消了在使用 Node 模块时使用该标志的要求。不过,在 Node.js 中使用 ESM 仍然会在终端中出现警告,提示该功能是试验性的。自 2020 年 4 月发布 14.0.0 版后,该警告不再出现。要在 Node.js 中使用 ESM,我们可以在 package.js 中添加 "type"(类型)和 "module"(模块):"模块"。

有趣的是,如果您习惯于使用 Webpack 等工具,那么 Node.js 中的 ESM 实现可能会略有不同。我们必须提供导入模块的完整路径,包括文件扩展名。


TypeScript 中的 ECMAScript 模块

早在 2015 年的 TypeScript 1.5 中,TypeScript 就开始支持 ECMAScript Modules 语法。需要了解的是,它默认将我们的代码移植到使用 CommonJS 的引擎盖下。

import { sum } from './sum';

sum(1, 2);

使用默认配置将上述文件转换为 JavaScript 时,我们可以看到 CommonJS。

"use strict";
exports.__esModule = true;
var sum_1 = require("./sum");
(0, sum_1.sum)(1, 2);
TypeScript 添加了 __esModule 标记,以表明文件是从 ESM 编译到 CommonJS 的。

要在 TypeScript 和 Node.js 中使用 ESM,我们需要稍微修改一下 tsconfig.json 文件。

tsconfig.json

{
"compilerOptions": {
"strict": true,
"module": "NodeNext",
"outDir": "dist"
}
}

由于使用了 NodeNext,TypeScript 将使用 ESM 导入和导出,而不是 CommonJS。请务必记住,Node.js 要求我们提供导入模块的完整路径。当我们使用希望在 Node.js 环境中运行的 TypeScript 时,也必须这样做。

与直觉相反的是,即使我们在编写 TypeScript 代码,也必须提供包含 .js 扩展名的路径。

import { sum } from './sum.js';

console.log(sum(1, 2));

完成所有这些后,我们就可以拥有一个在 Node.js 中运行并在引擎盖下使用 ECMAScript 模块的 TypeScript 应用程序。


管理依赖关系

在处理依赖关系时,在 CommonJS 和 ECMAScript 模块之间做出选择的问题会变得更加复杂。

使用 ECMAScript 模块的项目可以使用导入语法使用 CommonJS 模块。但是,使用 CommonJS 的项目除了通过异步导入函数外,不能以任何方式导入仅使用 ESM 的模块。正因为如此,许多使用 ECMAScript 编写 JavaScript 库的开发人员决定同时发布 CommonJS 和 ESM 代码。这样,他们的库就可以兼容任何一种模块系统。

然而,并非所有开发人员都愿意处理发布同时适用于 CommonJS 和 ESM 的软件包的麻烦。只发布与 ESM 兼容的库版本的做法越来越流行。因此,我们需要了解 CommonJS 和 ESM 的工作原理及其局限性。


总结

在本文中,我们介绍了 CommonJS 和 ECMAScript 模块的工作原理。我们了解了它们的语法以及如何在 Node.js 和 TypeScript 中使用它们。由于并非所有开发人员都愿意费尽周折地发布同时适用于 CommonJS 和 ESM 的包,因此了解这两种模块系统之间的区别变得越来越重要。有了这些知识,我们才能更好地为项目选择合适的模块系统,并适应与之相关的任何挑战。


链接: https://www.fly63.com/article/detial/12693

ES6模块功能:export和import的加载方式

ES6之前已经出现了js模块加载的方案,最主要的是CommonJS和AMD规范。commonjs主要应用于服务器,实现同步加载,如nodejs。AMD规范应用于浏览器,如requirejs,为异步加载。

Node的https模块_创建HTTPS服务器

Node的https模块:HTTPS服务器使用HTTPS协议,需要证书授权,SSL安全加密后传输,使用443端口

如何让 node 运行 es6 模块文件,及其原理

最新版的 node 支持最新版 ECMAScript 几乎所有特性,但有一个特性却一直到现在都还没有支持,那就是从 ES2015 开始定义的模块化机制。而现在我们很多项目都是用 es6 的模块化规范来写代码的,包括 node 项目

module、export、require、import的使用

module每个文件就是一个模块。文件内定义的变量、函数等等都是在自己的作用域内,都是自身所私有的,对其它文件不可见。在module中有一个属性exports,即:module.exports。它是该模块对外的输出值,是一个对象。

Node.js - 模块系统

模块是Node.js 应用程序的基本组成部分,文件和模块是一一对应的。换言之,一个 Node.js 文件就是一个模块,这个文件可能是JavaScript 代码、JSON 或者编译过的C/C++ 扩展。Node.js 提供了 exports 和 require 两个对象

ES模块基础用法及常见使用问题

ES6中引入了模块(Modules)的概念,相信大家都已经挺熟悉的了,在日常的工作中应该也都有使用。本文会简单介绍一下ES模块的优点、基本用法以及常见问题。

ES6 export 和 export default的区别

ES6中 export 和 export default 与 import使用的区别,使用 react native 代码详解,现在流行的前端框架,angular+ 主要使用 export 导出模块,react native 中使用 export default 导出模块,如今编辑器非常强大,安装插件会自动弹出模块名称,知道其导出怎么使用就可以了

export和export default的区别

export与export default均可用于导出常量、函数、文件、模块;你可以在其它文件或模块中通过import+(常量 | 函数 | 文件 | 模块)名的方式,将其导入,以便能够对其进行使用;

关于export和export default你不知道的事

网上有很多关于export和export default的文章,他们大部门都是只讲了用法,但是没有提到性能,打包等关键的东西。大家应该应该能理解import * from xxx会把文件中export default的内容都打包到文件中,而import {func} from xxx只会把文件中的func导入

最全的前端模块化方案

模块化主要是用来抽离公共代码,隔离作用域,避免变量冲突等。将一个复杂的系统分解为多个模块以方便编码。会讲述以下内容:CommonJS、AMD 及 核心原理实现、CMD 及 核心原理实现

点击更多...

内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!