一、模块
模块 就是一个js 文件,它实现了一部分功能,并且隐藏自己的内部实现,同时提供部分接口供其他模块使用。
模块有两个核心要素:
-
隐藏:模块内部实现
-
暴露:供外部使用的接口
任何一个正常的模块化标准,都应该 默认隐藏模块中的所有实现,而通过一些语法 或api 调用 来暴露接口。
1.1 模块导出
- 接口暴露的过程 就是模块的导出。
1.2 模块导入
-
当需要使用一个模块是,使用的是该模块暴露的部分(导出的部分),隐藏的部分是永远无法使用的
-
当通过某种语法 或 api 去使用一个模块时,这个过程就是模块导入
二、CommonJS 规范
2.1 commonJS 使用 exports 导出模块, require 导入模块
具体规范如下:
- 如果 js 文件中存在 exports 或 require,该 js 文件 是一个模块。
- 模块内的所有代码均为 隐藏代码,包括 全局变量、全局函数,这些全局的内容 均不应该对全局变量造成污染。
- 如果一个模块需要暴露一些API给外部使用,需要通过exports 导出,exports 是一个空对象,你可以为该对象 添加任何需要导出的内容。
- 如果一个模块需要导入其他模块,通过require 实现,require 是一个函数,传入模块的路径即可返回该模块导出的整个内容。
// index.js
const moduleA = require(‘./a.js’); // 导入模块 使用相对路径,并且以./ 或 ../ 开头
console.log(moduleA); // {a: 'a'}
// a.js
exports.a = 'a';
2.2 nodes 对 CommonJS 的实现
为了实现CommonJS 规范, nodejs 对模块 做出了以下处理
1、为了保证高效的执行,进加载必要的模块。nodejs 执行到require 函数时才会加载并执行。
2、为了隐藏模块中的代码,nodejs 执行模块时,会将模块中的所有放到一个函数中执行,以保证不污染全局变量。
(function(){
// 模块中的代码
})()
3、为了保证顺利的导出模块内容,nodejs 做了以下处理
- 在模块开始执行前,初始化一个值 module.exports = {}
- module.exports是模块导出之
- 为了方便开发者便捷的导出,nodejs 在初始化 完成module.exports后,有声明了一个变量exports = module.exports
(function(module){
module.exports = {};
var exports = module.exports
// a.js 写入的代码
exports.a = 'a';
return module.exports;
})()
// 所以 我们可以在 a.js 写法
exports.a = 'a';
exports.b = 'b';
// 或
module.exports = {
a: 'a',
b: 'b'
}
// or
module.exports = 'a' // 在index.js 打印 就是 a
module.exports = 'b'
// 注意 无效
exports = ‘a’;
JavaScript 导出代码:
exports.a = 1
exports.b = 2
exports.c = 3
JavaScript 导入代码:
const { a, b, c } = require('./uppercase.js')
- 为了避免反复加载同一个模块,nodejs 默认开启了模块缓存功能,如果已经加载过的模块,则会自动使用之前的导出结果。
// a.js
console.log('a 模块加载')
module.exports = 'a'
// b.js b模块中依赖a模块
console.log('b 模块加载')
const moduleA = require('./a.js');
module.exports = 'b'
// index.js 入口文件 依赖 a模块 和 b模块
const moduleA = require('./a.js');
const moduleB = require('./b.js');
打印结果为:
a 模块加载
b 模块加载
三、ES6规范
3.1 只需要在变量或函数前面加 export
关键字即可
//------ lib.js ------
export const sqrt = Math.sqrt;
export function square(x) {
return x * x;
}
export function diag(x, y) {
return sqrt(square(x) + square(y));
}
//------ main.js 使用方式1 ------
import { square, diag } from 'lib';
console.log(square(11)); // 121
console.log(diag(4, 3)); // 5
//------ main.js 使用方式2 ------
import * as lib from 'lib';
console.log(lib.square(11)); // 121
console.log(lib.diag(4, 3)); // 5
我们也可以直接导出一个列表,例如上面的lib.js可以改写成:
//------ lib.js ------
const sqrt = Math.sqrt;
function square(x) {
return x * x;
}
function add (x, y) {
return x + y;
}
export {sqrt, square, add}
3.2 Default exports (导出一个默认 函数/类)
这种方式比较简单,一般用于一个类文件,或者功能比较单一的函数文件使用。一个模块中只能有一个export default默认输出。
export default与export的主要区别有2个:
- 不需要知道导出的具体变量名
- 导入(import)时不需要{}
//------ myFunc.js ------
export default function () { ... };
//------ main.js ------
import myFunc from 'myFunc';
myFunc();
导出一个类
//------ MyClass.js ------
class MyClass{
constructor() {}
}
export default MyClass;
//------ Main.js ------
import MyClass from 'MyClass';
注意这里默认导出不需要用{}。
export default 和 export 区别
1、export与export default均可用于导出常量、函数、文件、模块等
2、你可以在其它文件或模块中通过import+(常量 | 函数 | 文件 | 模块)名的方式,将其导入,以便能够对其进行使用
3、在一个文件或模块中,export、import可以有多个,export default仅有一个
4、通过export方式导出,在导入时要加{ },export default则不需要