这块标准是在 2009 年提出来的,包含模块、IO、文件等。通过 Node.js 采用并作调整,因此提及 CommonJS 一般是 Node.js 中的版本了。在使用 Node.js 包管理器的年代,CommonJs 成为一颗有流量的明星了。webpack
CommonJs 的模块自然有自身的做用域,全部变量和函数声明只能本身访问,外人想都别想,这个保护机制太 nice 了。git
// order.js
const name = '订单系统';
复制代码
// index.js
const name = '首页';
require('./order.js');
console.log(`------\n${name}`);
复制代码
模块对外暴露的方式。对于要暴露的内容可以使用 module.exports
来导出,其导出内容格式是一个对象。也可以使用简化形式 exports
es6
// module.exports.js
module.exports = {
name: '订单系统',
total: (a, b) => {
return (a * b);
}
}
复制代码
// exports.js
exports.name = '订单系统';
exports.total = (a, b) => {
return (a * b);
}
复制代码
上面两种所要表达的功能是同样的,内在逻辑是 exports
指向 module.exports
,module.exports
是初始化时建的一个空对象。因此千万不要直接给 exports
赋值,还有 module.exports
和 exports
不要并存。上面第二个文件 exports.js
可这么来理解:github
// 初始化(便于理解 exports 与 module.exports 的关系)
const module = {
exports: {}
};
const exports = module.exports;
// exports.js
exports.name = '订单系统';
exports.total = (a, b) => {
return (a, b);
}
复制代码
经过 require
导入。web
// 04/src/order.js
console.log('模块文件 order.js');
exports.name = 'order name';
module.exports = {
name: '订单系统',
total: (a, b) => {
return a * b;
}
};
exports.title = 'order title';
复制代码
// 04/src/index.js
const title = require('./order.js').title;
const name = require('./order.js').name;
console.log(`exports.name 能展现么?-------\n${name}`);
console.log(`exports.title 能展现么?-------\n${title}`);
const total = require('./order.js').total;
setTimeout(function() {
console.log(`module.exports 能展现么?${total(10, 10)}`);
}, 3000);
console.log('动态加载');
const modulesName = ['product.js', 'shopcar.js'];
modulesName.forEach(name => {
require(`./${name}`).name();
});
复制代码
1.缓存加载,第二次导入文件时,无需加载,由于第一次导入已经加载过了,第二次直接使用上次导入的结果; 发现没有?这是 order.js 文件
这个通知在控制台里面只打印了一次,而文件 order.js
实打实的引入了两次。其原理是:咱们已经知道导出文件有 module
这个对象,咱们可能不知道的是这个对象有 loaded
这么个属性(记录模块是否被夹在过),其默认值是 false
,即没有加载过。当该模块第一次被加载后,loaded
值会变为 true
,因此第二次引入该模块就不会加载该模块了。缓存
2.加载模块支持动态加载;app
3.exports
和 module.exports
不要混合使用,不然 exports
会失效哦;函数
完整代码可查看目录 04 =>O(∩_∩)O~工具
ES6 Module 一样是将每一个文件做为一个模块,模块自身有其做用域。所不一样的是,引入了关键字 import
(导入)和 exports
(导出),例子仍是前面的例子,语法糖发生了变化。post
1.默认导出,上面例子咱们都已接触过了。不过每次只能导出一个,可直接导出对象、字符串、函数等。
// 导出字符串
export default '订单系统';
复制代码
// 导出对象,05/src/order.js
console.log('模块文件 order.js');
export default {
name: '订单系统',
total: (a, b) => {
return a * b;
}
};
复制代码
2.命名导出,可以使用关键字 as
对导出变量重命名。
// 方式一,05/src/order1.js
export const name = '订单系统1';
export const total = (a, b) => {
return a * b;
};
复制代码
// 方式二,,05/src/order2.js
const name = '订单系统2';
const total = (a, b) => {
return a * b;
};
export { name, total as getTotal };
复制代码
使用关键字 import
导入,也可以使用关键字 as
对导入变量重命名,也可以使用 as
将导入变量的属性添加到后面对象(order1
)中。
// 方式一
import { name, total as getProduct } from './order1';
复制代码
// 方式二
import * as order2 from './order2';
复制代码
// 05/src/index.js
import order from './order';
import { total as getProduct } from './order1';
import * as order2 from './order2';
console.log(`order.name: ${order.name}`);
console.log(`order1 getProduct: ${getProduct(10, 10)}`);
console.log(`order2 getTotal: ${order2.getTotal(10, 10)}`);
复制代码
完整代码可查看目录 05 =>O(∩_∩)O~
二者本质区别在于:CommonJS 对模块依赖是“动态”的,ES6 Module 是“静态”的。
1.动态,模块依赖关系是在代码运行阶段
require
路径可动态指定;2.静态,模块依赖关系是在代码编译阶段
咋一看,CommonJS 完美 KO ES6 Module 的方式。可事实并不是如此,ES6 Module 这种“静态”方式有优点:
场景:导入一个模块时,不一样模块模式是不同的。
// 06/src/commonJs.js
let csCount = 0;
module.exports = {
csCount,
csCountAdd: () => {
csCount += 10;
}
};
复制代码
// 06/src/es6-module.js
let esCount = 0;
const esCountAdd = () => {
esCount += 10;
};
export { esCount, esCountAdd };
复制代码
// 06/src/index.js
// CommonJS Module
let csCount = require('./commonJs').csCount;
let csCountAdd = require('./commonJs').csCountAdd;
console.log(`----commonjs 初次加载----\n${csCount}`);
csCountAdd();
console.log(`----commonjs 内部自加 10 -----\n${csCount}`);
csCount += 20;
console.log(`----commonjs 启动项自加 20------\n${csCount}`);
// Es6 Module
import { esCount, esCountAdd } from './es6-module.js';
console.log(`----es6 初次加载----\n${esCount}`);
esCountAdd();
console.log(`----es6 内部自加 10 -----\n${esCount}`);
esCount += 20;
console.log(`----es6 启动项自加 20------\n${esCount}`);
复制代码
经过例子及上图运行结果,可剖析
csCount()
,可是并无形成在文件 index.js
中副本(csCount
)的影响;而副本(csCount
)在文件 index.js
中可更改;esCount()
,文件 index.js
中副本(esCount
)的也随之变化;而副本(esCount
)在文件 index.js
中是不可更改,便是只读的;完整代码可查看目录 06 =>O(∩_∩)O~
一般工程中是应该尽可能避免这种恶心的循环依赖的产生,由于会带来复杂度。可尽管咱们知道这是很差的,也理性地避免发生。但链条长了,业务多了,人员多了,仍是不知不觉中“造孽般地”写出这样的代码。
场景: A 依赖 B,B 依赖 C,C 依赖 D,D 依赖 A。其实 A 与 B 就互相依赖了。
// 07/src/a.js
const fileB = require('./b.js');
console.log(`----- a.js 文件展现 b.js 内容 ------\n`, fileB);
module.exports = '这是 a.js 文件';
复制代码
// 07/src/b.js
const fileA = require('./a.js');
console.log(`----- b.js 文件展现 a.js 内容 ------\n`, fileA);
module.exports = '这是 b.js 文件';
复制代码
// 07/src/index.js
require('./a.js');
复制代码
咱们脑海中自运行结果是
----- b.js 文件展现 a.js 内容 ------
这是 a.js 文件
----- a.js 文件展现 b.js 内容 ------
这是 b.js 文件
复制代码
可控制台是
反复检查了代码,没错啊,可本该显示 这是 a.js 文件
,为什么展现 {}
?不行,我仍是要好好捋一捋
index.js
导入文件 a.js
,开始执行文件 a.js
,第一行导入文件 b.js
,此时进入文件 b.js
内部;b.js
第一行又导入文件 a.js
,循环依赖由此产生。这时执行权并无交回给文件 a.js
,而是直接取导出值,此刻文件 a.js
还未执行技结束,导出值就默认为空对象,所以文件 b.js
执行打印语句时,显示 ----- b.js 文件展现 a.js 内容 ------ {}
;b.js
执行结束了,执行权接着交回给文件 a.js
, 文件 a.js
继续执行第二行,而后在控制台打印 ----- a.js 文件展现 b.js 内容 ------ 这是 b.js 文件
。至此,这个流程结束;完整代码可查看目录 07 =>O(∩_∩)O~
// 08/src/a.js
import fileB from './b.js';
console.log(`----- a.js 文件展现 b.js 内容 ------\n`, fileB);
export default '这是 a.js 文件';
复制代码
// 08/src/b.js
import fileA from './a.js';
console.log(`----- b.js 文件展现 a.js 内容 ------\n`, fileA);
export default '这是 b.js 文件';
复制代码
// 07/src/index.js
import a from './a.js';
复制代码
咱们脑海中自运行结果天然也是
----- b.js 文件展现 a.js 内容 ------
这是 a.js 文件
----- a.js 文件展现 b.js 内容 ------
这是 b.js 文件
复制代码
可控制台是
完整代码可查看目录 08 =>O(∩_∩)O~
我晕,仍是不对,此次不是空对象了,倒是 undefined
。那循环依赖该如何解决呢?
---> 等我下篇 <---