Node项目中 CommonJs语法 和ES6 Module的语法 ,两个不同语法的文件可以互相导入吗?

作者站长头像
站长
· 阅读数 18

现象

  1. 项目A:ESM 模块文件中导入 CommonJS 模块文件。运行项目没报错

    https://stackblitz.com/edit/stackblitz-starters-pescaa?file=p...
  2. 项目B:CommonJS 模块文件中导入 ESM 模块文件,运行项目报错

    https://stackblitz.com/edit/stackblitz-starters-hujdvx?file=p...

关于

https://nodejs.org/api/packages.html 中有这样一段内容:

Node项目中 CommonJs语法 和ES6 Module的语法 ,两个不同语法的文件可以互相导入吗?

如果以模块的形式导入会使用模块自己的 package.json. 如果以路径的形式化导入会使用被导入文件就近的 package.json.我的这个理解对不对?


问题

我一时也不知道该怎么表述这个问题了。大概就是 ESM 和 CommonJS 互相导入的问题吧。

  1. 项目是 ESM,那么项目的某些文件中能不能用 CommonJS、然后在 ESM 中导入 CommonJS?或者项目是 CommonJS,项目的某些文件是 ESM,CommonJS 中能不能导入 ESM?
  2. import 本地项目的某一个文件,为什么会使用导入文件附近的 package.json?被导入文件附近的 pacakge.josn 有什么影响?Node项目中 CommonJs语法 和ES6 Module的语法 ,两个不同语法的文件可以互相导入吗?
  3. 如果是导入模块,是不是一般模块的 package.json 中都会指明使用 CommonJS 语法会导入哪个文件、使用 ESM 语法会导入哪个文件?

===============================================

对于以上的问题 @然后去远足 已经给出了准确的回答。

补充两个问题

另外就是 在 https://nodejs.org/api/packages.html 的开头部分 ES 模块和CommonJS模块的对比中提到了, ES可以 loader commonjs, commonjs不能loader es

Node项目中 CommonJs语法 和ES6 Module的语法 ,两个不同语法的文件可以互相导入吗?Node项目中 CommonJs语法 和ES6 Module的语法 ,两个不同语法的文件可以互相导入吗?

补充问题:

1.至于为什么 commonjs 不能loader es,我不确定,chatgpt的回复

Node项目中 CommonJs语法 和ES6 Module的语法 ,两个不同语法的文件可以互相导入吗?@然后去远足 求大佬再帮忙解释一下。

2.让chatgpt给个示例, 他给出的示例中 提到的输出顺序 也搞不懂为什么 顺序不确定。

Node项目中 CommonJs语法 和ES6 Module的语法 ,两个不同语法的文件可以互相导入吗?Node项目中 CommonJs语法 和ES6 Module的语法 ,两个不同语法的文件可以互相导入吗?

按照我的理解: commonjs的require是同步的,es的import也是同步的。所以main.js 中 require('./commonjsModule.js');commonjsModule.js中 require('./es6Module.mjs');

所以 es6Module.mjs 不是应该先被加载并执行内部的代码吗?

回复
1个回答
avatar
test
2024-06-27

A1:

可以混用,但最好不要这么做。

ESM 里引入 CommonJS 的话就正常 import 就好了,没什么特殊的。

CommonJS 里引入 ESM 稍微有点儿特殊,需要用 dynamic import 而不是 require()

- let bModule = require('./pathA/pathB/b.js');
- console.log('bModule', bModule);

+ import('./pathA/pathB/b.js').then((bModule) => {
+   console.log('bModule', bModule);
+ });

A2:

.mjs.cjs 文件分别以何种方式加载这没什么争议,有问题的是 .js 这种文件。

因为模块加载器需要提前知道一个 .js 到底是 CommonJS 还是 ESM 的、好来决定用哪种方式去加载,但显然通过文件名本身是没办法知道的,所以变成了通过 package.json 来区分。优先会找你导入的那个模块同级目录下的 package.json、如果没有那就向上一级目录找、还没有就再向上 …… 直到项目根目录为止,此时也就是跟你项目本身的设置保持一致了。


A3:

如果是只支持一种模块方案的,那就在 package.json 里配置 type 这个字段来标明。不标明默认就是 CommonJS,毕竟要跟以前的 Node 项目兼容 —— 老项目肯定都是只支持 CommonJS 的。

如果两种都支持,Node v14 之后 package.json 有了 exports 这个新的配置,你可以通知配置 CommonJS 和 ESM 两种模块的指向。新一点的库一般都是这种方式了,你会发现它的项目结构里同时有 eslib 两个子目录,其实就分别代表 ESM 和 CommonJS,然后 exports 里会分别指向这两个目录。

当然了,开发的时候都是按一种方式去开发,最后通过 Webpack、Rollup 之类的构建工具转译成两种模块方案;而不是写两遍代码。

回复
likes
适合作为回答的
  • 经过验证的有效解决办法
  • 自己的经验指引,对解决问题有帮助
  • 遵循 Markdown 语法排版,代码语义正确
不该作为回答的
  • 询问内容细节或回复楼层
  • 与题目无关的内容
  • “赞”“顶”“同问”“看手册”“解决了没”等毫无意义的内容