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

现象

  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 中有这样一段内容:

image.png

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


问题

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

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

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

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

补充两个问题

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

image.png
image.png

补充问题:

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

image.png
@然后去远足 求大佬再帮忙解释一下。

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

image.png
image.png

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

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

阅读 2.6k
1 个回答

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 之类的构建工具转译成两种模块方案;而不是写两遍代码。

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题
宣传栏