likes
comments
collection
share

了解CommonJS规范与ES6 Module的差异与共同之处

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

模块机制诞生的背景

JavaScript语言自身的缺陷

说起JavaScript语言,是一个典型的从弱小到壮大的奋斗史。起初,它只是一个非常不起眼的语言,用来处理非常小众的问题。从设计之初,目标就是解决一些脚本语言的问题,因为需要处理的场景没那么复杂(最初是考虑用作表单校验和网页特效),且是仓促地被创造出来,所以它自身的各种缺陷和缺点也被各种编程人员广为诟病。

随着B/S架构的市场覆盖率越来越大,基于浏览器的系统日益增多,Javascript也被寄予了更多的期待,Javascript也从表单校验跃迁到应用开发的级别。在这个过程中,它大致经历了工具类库、组件库、前端框架、前端应用的变迁。

了解CommonJS规范与ES6 Module的差异与共同之处

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之间的关系,共同构成了繁荣的生态系统。

了解CommonJS规范与ES6 Module的差异与共同之处

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。

模块的意义在于将类聚的方法和变量等限定在私有的作用域中,同时支持引入和导出功能以顺畅地链接上下游的依赖。

了解CommonJS规范与ES6 Module的差异与共同之处

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
评论
请登录