[JS] 模块系统ESM和CJS
简要说明二者的表象区别:
ESM 是ES6之后的JS模块规范。
CJS (CommonJS)是社区发展起来的主要应用于node.js应用的模块规范。
ESM模块规范使用import导入,export 、export default导出。
CJS模块规范使用require导入和exports、module.exports导出。
在一个npm init初始化的Node.js项目中,package.json里面type字段的设置决定.js文件用哪种模块规范加载。(type不写-> CJS加载。"type": "module" -> ESM加载)。此外.mjs文件总是以 ES6 模块加载,.cjs文件总是以 CJS 模块加载。
重点说一说两个模块系统在使用时的区别:
不论以CJS、ESM中的哪种模块系统作为模块化开发的规范,(大部分情况下)最终都需要经过编译工具处理,编译成js文件,才能最终应用到生产环境。
再说一说两种模块规范的不同:
// ↓ ESM (情景1)
// a.js
const a = 1;
const b = "2";
export { a, b };
export default function() {
console.log("default function done");
}
// b.js
import { a, b } from "./a.js";
import defaultFunc from "./a.js";
console.log(a);
// 正常输出 1
console.log(b);
// 正常输出 "2"
defaultFunc();
// 正常执行 -> "default function done"
// ↓ CJS (情景2)
// aa.js
const a = 1;
const b = "2";
exports.a = a;
exports.b = b;
module.exports = 3;
// bb.js
const bb= require("./aa.js");
console.log(bb);
// 输出结果 3
对比情景1和情景2:
在ESM中
export default被视为顶级导出
export 被视为次级导出
两种导出方式不冲突,可以同时使用
在CJS中
module.exports被视为顶级导出
exports.x 被视为次级导出
顶级导出会覆盖次级导出(无论顶级导出的位置先后)
补充一个情景3
// ↓ CJS (情景3)
// cc.js
const a = 1;
const b = "2";
exports.a = a;
exports.b = b;
// dd.js
module.exports = 3;
// ee.js
module.exports = function() {
console.log("default function done");
}// ff.js
const cc = require('./cc.js');
const dd = require('./dd.js');
const ee= require('./ee.js');
console.log(cc);
// 正常输出 { a: 1, b: '2' }
console.log("dd->", dd);
// 正常输出 3
ee();
// 正常执行 -> "default function done"
上面的情景1、2、3基本涵盖了两个模块规范使用中出现的情况。ESM、CJS如果单独使用(即在一个工程里从头到尾使用ESM,或从头到尾使用CJS),都没有问题,编译工具会帮我们处理好各种情况。
但是一旦出现两种模块混用,就可能出现问题。两种模块混用,必然需要某种编译工具,使用的是编译后产物。
这里以ts-node为例。
// a.ts (ESM)
export default function () {
console.log("default function done")
}// b.ts (CJS)
const b = require("./a.ts");
//b();
// 直接执行顶级导出的函数会报错
b.default() // 必须使用default属性 -> 正常执行 "default function done"
工作中,我们尽量在单个工程中使用一种模块规范。如果真的遇到了上面的情景,需要ESM和CJS混用,最好是查阅工程所用的编译工具(如webpack、rollup、ts-node等等)的文档。看编译工具如何处理兼容问题。
举个例子,ts-node中的语法糖。
export = xxx;
import x = require('path/file');
完结。
【[JS] 模块系统ESM和CJS】同步更新到自己的语雀
https://www.yuque.com/diracke...
推荐阅读
- Angular - 模块AppModule导入的意外值MatDialog
- 如何使用mockito在安卓系统中创建模拟api响应[关闭]。
- 如何在Appium(安卓)上点到系统活动应用按钮()
- Android|Android 12(S) 图像显示系统 - GraphicBuffer同步机制 - Fence
- 面试|假如你想成为高级程序员、系统分析员(续) (转)
- ImportError(没有名为'app'的模块)
- Linux系统磁盘高级应用和Vi编译器
- Java熟食包点系统实战
- 14个用于渗透测试和数字取证的免费操作系统
- 在尝试连接到applet进行调试时,'shmemBase_attach失败(系统无法找到指定的文件')