CommonJS模块规范和ES6模块规范完全是两种不同的概念
module.exports
和exports
是属于commonJs规范,export
和export default
是ES6模块规范
require/exports
是 CommonJS/AMD 中为了解决模块化语法而引入的
import/export
是ES6引入的新规范,因为浏览器引擎兼容问题,需要在node中用babel
将ES6语法编译成ES5语法
一 CommonJS模块规范
1.1 基本使用
Node应用由模块组成,采用CommonJS模块规范。
根据这个规范,每个文件就是一个模块,有自己的作用域。在一个文件里面定义的变量、函数、类,都是私有的,对其他文件不可见。
CommonJS规范规定,每个模块内部,module变量代表当前模块。这个变量是一个对象,它的exports属性(即module.exports)是对外的接口。加载某个模块,其实是加载该模块的module.exports属性。
1 | var x = 5; |
上面代码通过module.exports
输出变量x和函数addX。
require方法用于加载模块。
1 | var example = require('./example.js'); |
- module.exports 初始值为一个空对象 {}
- exports 是指向的 module.exports 的引用
- require() 返回的是 module.exports 而不是 exports
1.2 exports 与 module.exports
优先使用module.exports
,不建议同时使用 exports
和 module.exports
。如果先使用 exports
对外暴露属性或方法,再使用module.exports
暴露对象,会使得 exports
上暴露的属性或者方法失效。原因在于exports
仅仅是 module.exports
的一个引用
为了方便,Node为每个模块提供一个exports
变量,指向module.exports
。这等同在每个模块头部,有一行这样的命令。
1 | var exports = module.exports; |
exports
等于 module.exports
,相当于在js文件头部,有一个module对象,module.exports = exports;
exports
是一个对象,所以可以exports
多个值
1 | // 暴露.js |
于是我们可以直接在 exports 对象上添加方法,表示对外输出的接口,如同在module.exports上添加一样。
注意,因为 Node 模块是通过 module.exports 导出的,如果直接将exports变量指向一个值,就切断了exports与module.exports的联系,导致意外发生:
1 | // a.js |
示例1
导出:
1 | //output.js |
导入:
1 | // input.js |
示例2
导出:
1 | //output.js |
导入:
1 | // input.js |
示例三
exports.xxx = xxx
和 module.exports = xxx;
一起使用时, exports.xxx
的属性会失效
导出:
1 | //output.js |
导入:
1 | // input.js |
在上面的console中, exports.name = 'hhhh'
的属性被 module.exports
覆盖了,所以失效了.
1.3 module 对象
NodeJs 内部提供一个 Module 构建函数。所有模块都是 Module 的实例。
每个模块内部,都有一个 module 对象,代表当前模块。它有以下属性。
module 对象的属性
module.id
模块的识别符,通常是带有绝对路径的模块文件名。module.filename
模块的文件名,带有绝对路径。module.loaded
返回一个布尔值,表示模块是否已经完成加载。module.parent
返回一个对象,表示调用该模块的模块(程序入口文件的module.parent为null)module.children
返回一个数组,表示该模块要用到的其他模块。module.exports
表示模块对外输出的值。
module.exports 属性
module.exports
属性表示当前模块对外输出的接口,其他文件加载该模块,实际上就是读取module.exports
变量。module.exports
属性表示当前模块对外输出的接口,其他文件加载该模块,实际上就是读取module.exports
变量。- exports 变量
我们有时候会这么写:
1 | // test.js |
这样也可以拿到正确的结果,这是因为:exports 变量指向 module.exports。这等同在每个模块头部,有一行这样的命令。
1 | var exports = module.exports; |
注意:不能直接给 exports 变量赋值,这样会改变 exports 的指向,不再指向 module.exports。在其他模块使用 require 方法是拿不到赋给 exports 的值的,因为 require 方法获取的是其他模块的 module.exports 的值。
建议:尽可能的使用 module.exports
来导出结果。
1.4 require
NodeJs 遵循 CommonJS 规范,该规范的核心是通过 require来加载其他依赖的模块。
require 是 node 用来加载并执行其它文件导出的模块的方法。
- 通过
require
引入基础数据类型时,属于复制该变量 - 通过
require
引入复杂数据类型时, 属于浅拷贝该对象
在导出的文件中使用module.exports
对模块中的数据导出,内容类型可以是字符串,变量,对象,方法等不予限定。使用require()
引入到需要的文件中即可
在模块中,将所要导出的数据存放在module
的export
属性中,在经过CommonJs/AMD规范的处理,在需要的页面中使用require
指定到该模块,即可导出模块中的export
属性并执行赋值操作(值拷贝)
1 | // module.js |
1 | // sample.js |
当我们不需要导出模块中的全部数据时,使用大括号包含所需要的模块内容。
1 | // module.js |
1 | // sample.js |
二 ES6模块规范
每一个模块只加载一次, 每一个JS只执行一次, 如果下次再去加载同目录下同文件,直接从内存中读取。 一个模块就是一个单例,或者说就是一个对象;
每一个模块内声明的变量都是局部变量, 不会污染全局作用域;
模块内部的变量或者函数可以通过export导出;
一个模块可以导入别的模块
2.1 基本使用
2.1.1 import
import是在编译过程中加载,也就是说是在代码执行前执行,比如说,import后面的路径写错了,在运行代码前就会抛错,所以在编写代码时,必须放在模块顶部(import是静态执行的).
不同于CommonJS,ES6使用 export 和 import 来导出、导入模块。
1 | // profile.js |
需要特别注意的是,export命令规定的是对外的接口,必须与模块内部的变量建立一一对应关系。
1 | // 写法一 |
2.1.2 import 的导入方式
1 | import foo from './output' |
2.2 es6导出、导入
2.2.1 基本使用
export { xx, xx}
与import {xx, xx} from '../../xxx.js'
1 | // 暴露.js |
2.2.2 别名
在export接口的时候, 我们可以使用 XX as YY修改导出的接口名字
1 | // 暴露.js |
2.2.3 特定引用导出
直接在export的地方定义导出的函数
1 | // 举个vue组件懒加载的例子 |
2.2.4 export default导出
1 | // 如果一个js模块文件就只有一个功能, 那么就可以使用export default导出 |
2.2.5 通配符导入
1 | // 暴露.js |
在import
的时候可以使用通配符导入外部的模块:
import * as xxx from ‘xxx’
: 会将若干export导出的内容组合成一个对象返回;import xxx from ‘xxx’:(export default Din)
只会导出这个默认的对象作为一个对象
2.3 export default 命令
export default导出,这种导出的方式不需要知道变量的名字, 相当于是匿名的;
1 | // export-default.js |