了解CommonJS规范与ES6 Module的差异与共同之处
模块机制诞生的背景
JavaScript语言自身的缺陷
说起JavaScript语言,是一个典型的从弱小到壮大的奋斗史。起初,它只是一个非常不起眼的语言,用来处理非常小众的问题。从设计之初,目标就是解决一些脚本语言的问题,因为需要处理的场景没那么复杂(最初是考虑用作表单校验和网页特效),且是仓促地被创造出来,所以它自身的各种缺陷和缺点也被各种编程人员广为诟病。
随着B/S架构的市场覆盖率越来越大,基于浏览器的系统日益增多,Javascript也被寄予了更多的期待,Javascript也从表单校验跃迁到应用开发的级别。在这个过程中,它大致经历了工具类库、组件库、前端框架、前端应用的变迁。
JavaScirpt不断被类聚和抽象,以便更好组织业务逻辑。但是,在这个过程中就发现,JavaScript缺少了一项功能:模块。对比其他编程语言,Java有类文件,Python有import机制,PHP有include和require。JavsScript啥都没有啊,即使可以通过<script>
引入不同的js文件,但是终究是不太优雅的。
所以,在JavaScript中增加模块机制就是一个很重要的事情了。JavaScript社区就开始了JavaScript模块规范的制定,其中CommonJS规范的提出算是最为重要的里程碑,服务端的JavaScript——Node.js就是用的CommonJS进行模块管理。
CommonJS规范
1. CommonJS的愿景
CommonJS的愿景——希望JavaScript能够在任何地方运行。在实际的应用中,JavaScript的表现能力取决于宿主环境中的API支持程度。
最初JavaScript只能运行在浏览器,服务端的JavaScript在Node.js出来之前基本没啥水花。而Node.js的成功也离不开CommonJS规范,Node借鉴CommonJS的Modules规范实现了一套非常易用的模块系统。
Node与浏览器以及W3C组织、CommonJS组织、ECMAScript之间的关系,共同构成了繁荣的生态系统。
2. CommonJS的模块规范
CommonJS的模块规范主要有模块引用、模块定义和模块标识三个部分。
2.1 模块引用
const events = require('events');
在CommonJS规范中,存在require()方法,这个方法接受模块标识,以此引入一个模块的API到当前上下文中。
2.2 模块定义
CommonJS规范中,exports对象用于导出当前模块的方法或者变量,并且它是唯一导出的出口。在模块中,还存在一个module对象,它代表的的是模块自身,而exports是modules的属性。
在Node中,一个文件就是一个模块,将方法挂载在exports对象上作为属性即可定义导出的方式。
// test.js
exports.getRangeValue = generateRangeValue;
function generateRangeValue (min, max) {
// 最小值是min,所以加min
// 制定范围所以用随机数 * 区间数
return Math.floor(Math.random() * (max - min)) + min;
};
在另一个文件中,通过require()方法引入即可使用这个方法了
// index.js
const { getRangeValue } = require('./test')
console.log(getRangeValue(1,10))//2
2.3 模块标识
模块标识其实就是传递给require()方法的参数,它必须符合小驼峰命名的字符串,或者以.
、..
开头的相对路径,或者绝对路径。它可以没有文件后缀名.js。
模块的意义在于将类聚的方法和变量等限定在私有的作用域中,同时支持引入和导出功能以顺畅地链接上下游的依赖。
ES6模块
在CommonJS和AMD这样的模块化机制被广泛使用了之后,浏览器也坐不住了,感觉自己也应该支持模块化,浏览器原生的模块机制一般称为JavaScript模块
,因为是在ES6标准实现的,所以大家一般称之为ES6module
。
Javascript模块的设计和CommonJS差不多,重点都是为了实现导入导出。
1. export导出
// test.mjs
export function generateRangeValue(min, max) {
// 最小值是min,所以加min
// 制定范围所以用随机数 * 区间数
return Math.floor(Math.random() * (max - min)) + min;
}
export default function sayHi () {
console.log("Hello world!");
}
可以用export
关键字定义导出的内容,export default
可以定义这个模块的默认导出,就无须关心里面的内容其啥名了,可以在导入的地方对默认导出起别名。
2. import导入
// index.mjs
// 这里可以对默认导出起别名,也可以用as 对具名导出其别名
import hello, { generateRangeValue as getRangeValue} from './test.mjs';
hello();
console.log(getRangeValue(1, 10));
import
关键词可以导入数据。
总结对比
1.CommonJS和ES6 module导出对象时,导出的都是值的引用(如果改变了导入的值会影响全局)
2.CommonJS的默认导出(module.exports
)会覆盖其他的导出内容(可以理解为重新定义了module.exports对象),而ES6的默认导出和其他变量的导出可以共存
3.CommonJS 模块的require()
是同步加载模块,ES6 模块的import
命令是异步加载,有一个独立的模块依赖的解析阶段。
转载自:https://juejin.cn/post/7372911082586112034