为了账号安全,请及时绑定邮箱和手机立即绑定

如何把CommonJS项目改成ESM模块系统

(最初发布于 deno.com/blog。)

ECMAScript 模块(“ESM”)是编写和共享 JavaScript 的官方现代化方式——它在多种环境中都得到了支持(例如浏览器、现代运行时如 Deno),并且提供了更好的开发体验(例如异步加载和无需全局变量即可直接导出)。虽然 CommonJS 许多年以来一直是标准,但支持 CommonJS 实际上正在阻碍 JavaScript 社区的发展。

所有新的 JavaScript 代码都应使用 ESM 编写,以确保未来的兼容性。然而,出于与较新包兼容的原因,许多遗留的代码库需要现代化改造。这篇文章中,我们将教你如何把一个旧的 CommonJS 项目迁移到支持 ESM 的项目,并推荐一些工具来帮助你更顺利地完成这个迁移过程。

想要用现代的方式编写 JavaScript 和 TypeScript,而不必处理繁琐的配置或模板代码吗?

试试Deno,它是一个“开箱即用”的默认安全的一站式工具链,无需额外配置,支持原生的TypeScriptWeb 标准 API

注释:模块引入和输出

下面是将 CommonJS 的导入和导出更新为 ESM 的方法。

在出口方面:

// 添加数字函数
// function addNumbers(num1, num2) {  
// 导出添加数字函数
export function addNumbers(num1, num2) {  
  return num1 + num2;  
};  

// 导出模块
// module.exports = {  
//   addNumbers,  
// }

在进口这一方面:

    - const { addNumbers } = require("./add_numbers");
    + import { addNumbers } from "./add_numbers.js";

    console.log(addNumbers(2, 2));

(注:-+ 符号用于表示代码中的更改)

注意,在 ESM 中,模块路径必须包含文件扩展名。完整的导入路径可以减少歧义,确保每次解析时都能正确导入文件。此外,这与浏览器如何处理模块导入的方式相同,使得编写可预测且易于维护的同构代码变得更加容易。

那条件导入呢?如果你用的是 Node.js v14.8 或更高版本(或者 Deno),就可以用顶级 await 来让 import 变成同步的。

    - const module = boolean ? require("module1") : require("module2"); // 根据布尔值条件,使用require加载模块
    + const module = await (boolean ? import("module1") : import("module2")); // 根据布尔值条件,使用异步import加载模块,await确保异步操作完成
编辑一下 package.json 文件

如果你使用 package.json,你可能需要做一些调整来支持 ESM:

{  
  "name": "my-project",  
  "type": "类型",  
  "main": "index.js",  
  "exports": "./index.js",  
  // ...  
}

在 ESM 中,开头的 "./" 是必需的,因为每个引用路径都必须包含完整路径,包括目录和文件扩展名。

此外,"main""exports" 都定义了项目的入口。然而,"exports" 是较 "main" 更现代的选择,它支持多个入口点和环境间的条件解析,允许作者清晰地定义包的公共接口,并防止定义在 "exports" 之外的其他入口。

{  
  "name": "my-project",  
  "type": "模块类型",  
  "exports": {  
    ".": "./index.js",  
    "./other": "./other.js"  
  }  
}

最后,另一种让 Node 运行 ESM 文件的方式是使用 .mjs 文件扩展名。如果你只想将单个文件转换为 ESM,这非常有用。但如果你的目标是转换整个代码库为 ESM,那么更简单的方法是修改 package.json 文件中的 type

其他改动

由于在 ESM 中 JavaScript 会自动进入严格模式(https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Strict_mode),你可以从代码库中移除所有 "use strict";

    - "use strict";

CommonJS 还支持一些 ESM 中没有的内置全局变量(例如 __dirname__filename)。一种简单的解决办法是使用一个简单的 shim 来填补这些值的空缺:

    // Node 20.11.0+, Deno 1.40.0+ 版本以上
    const __dirname = import.meta.dirname;
    const __filename = import.meta.filename;

    // 之前是
    const __dirname = new URL(".", import.meta.url).pathname;
    import { fileURLToPath } from "node:url";
    const __filename = fileURLToPath(import.meta.url);
迁移工具包

虽然上述内容谈到将 CommonJS 代码库转到 ESM 所需的更改,还有一些工具可以帮助完成这一转换。

使用VSCode,你可以快速将所有 CommonJS 的导入和导出语句转换为 ESM。只需将鼠标悬停在 require 上,然后点击“快速修复”选项,该文件中的所有这些语句都将更新为 ESM。

VSCode提供了一个将CommonJS模块引入转换成ESM模块导入的快速解决方案。

你会发现 VSCode 可以自动替换导入和导出的正确关键字,但是这些指定符缺少文件扩展名。你可以通过运行[**deno lint --fix**](https://docs.deno.com/runtime/manual/tools/linter/)快速添加它们。Deno 的代码检查器自带了一个 no-sloppy-imports 规则,当导入路径中缺少文件扩展名时,该规则会显示代码检查错误。

为了更端到端地将 CommonJS 转换为 ESM,有几种转译选项。例如,可以使用 npm 包 cjs2esmcjstoesm,以及名为 babel-plugin-transform-commonjs 的 Babel 插件。不过,这些工具可能不再活跃维护且可能功能不全,所以在评估这些工具时,请注意这一点。

下一步会是啥

ESM 是 JavaScript 中分享代码的标准方式,所有新的 JavaScript 代码都应当支持 ESM。选择现在支持 CommonJS 对模块作者和不希望处理遗留兼容性问题的开发人员来说可能会非常痛苦。事实上,我们的开源现代 JavaScript 注册表 JSR 明确禁止使用 CommonJS。我们鼓励每个人都为提升 JavaScript 生态系统贡献自己的力量。

🚨️试试今天的 Deno 2 吧。🚨️

Deno 兼容 Node/npm,内置包管理器,一体化零配置工具套件 原生 TypeScript 支持功能 ,等等。

点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消