两种JS脚本 ESM(新)和CJS(旧)
- CJS使用require()、module.exports;
- ESM使用import、export。
- node中默认支持CJS语法 require
- ESM可以调用CJS脚本 但是只能默认引入 默认导出的CJS脚本
// CJS需要默认导出
export.export = 1
// ESM默认引入
import NUM from './cjs.js'
// 不能使用如下写法!!!!!
export.export = { num:1,age:18 }
import {num,age} from './cjs.js'
完全不同的ESModule和CommonJS
- CJS使用require()进行脚本引入 他会同步引用并执行模块代码 当JS脚本遇到require时会立即运行其引用的JS文件
const getName = require("./getName.js")
const name = getName({id:1}) // 执行到这里时立刻调用require(),并加载内部的脚本
- ESM会异步加载模块
(1)首先会解析脚本中的import和export等关键字 构建一个文件依赖图 (类似webpack的构建过程)
(2)然后会异步下载这些所有的依赖模块
(3)最后 ESM 会进入执行阶段,按顺序执行各模块脚本。
所以我们常常会说,CommonJS 模块是运行时加载,ES6 模块是编译时输出接口。
ESM会更改JS中的内容
- 首先ESM会默认使用严格模式 this不引用全局对象 作用域的工作方式也不同…
- JS脚本现在的默认设置依然是CJS
//所以需要在script标签中加上type='module' 才能支持ESM
<script type='module' src="..."/>
Node默认使用CJS 而Deno默认使用ESM 这导致了Deno的生态需要从零开始
ESM 无法使用 __filename 和 __dirname
这样一来在 Node.js 中,如果要把默认的 CJS 切换到 ESM,会存在巨大的兼容性问题
CommonJS模块输出是一个值的拷贝,ES6模块输出的值是引用
如何理解这句话呢
- 在Node中,所有的模块都会生成并且暴露出module.export对象, 我们引入的是module.export中暴露的变量,而不是原文件中的变量.
上图蓝色的num共用同一个引用,都指向module.export中的num(index,js中num的拷贝),
下图橙色的num直接指向index中的num
由上图可推出 在ESM中 源文件和引用的模块中的num会相互影响
在CJS中则不会 (因为中间多了一层module对象)