# 第2章 模块打包
# CommonJS
CommonJS在每个模块首部默认添加了以下代码:
var module = {
exports: {}
}
var exports = module.exports;
1
2
3
4
2
3
4
//test.js
var name = '1';
// index.js
require('./test.js');
1
2
3
4
5
2
3
4
5
test.js会形成一个属于模块自身的作用域,所有变量及函数外部不可见。
exports.name = 2;
module.exports = {
name: 1
}
1
2
3
4
2
3
4
export.name = 2;相当于在module.exports对象中加个name属性。
require('./test.js');
只会加载执行一遍。有个属性loaded用于记录该模块是否被加载过。默认是false,第一次被加载后变为true,后续检测到就不会加载了。
require('./test.js');
1
直接使用require可以把它的接口挂在全局对象上。
# ES6 Module
ES6 Module也是将每个文件作为一个模块,每个模块拥有自身的作用域,不同的是导入、导出语句。
export const name = "test";
// 写法2
const name = "test";
export { name as test}; // 导入时用test
1
2
3
4
5
2
3
4
5
export default {
name: 'test'
}
export default 'xxxx';
export default function () {};
1
2
3
4
5
2
3
4
5
import { default as myModule } from './test.js'
1
# CommonJS 和 ES6 Module 区别
# 动态与静态
CommonJS是动态的,模块依赖关系的建立发生在代码运行阶段。
ES6 Module是静态的,模块依赖关系的建立发生在代码编译阶段。
# ES6 Module优势
- 死代码检测和排除。
- 模块变量类型检查。
- 编译器优化。
# 值拷贝与动态映射
导入模块时,对于CommonJS来说获取到的是一份导出值的拷贝。ES6 Module中是值的动态映射,并且这个映射是只读的,如果需要修改的话,可以通过调用内部函数。
# 循环依赖
循环依赖是指模块A依赖于模块B,同时模块B依赖模块A.
# CommonJS
// foo.js
const bar = require('./bar.js');
console.log('value of bar', bar);
module.exports = "this is foo.js";
// bar.js
const foo = require('./foo.js');
console.log('value of foo', foo);
module.exports = "this is bar.js";
// index.js
require('./foo.js');
// output
// value of foo: {}
// value of bar: this is bar.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
执行顺序:
- index.js中导入foo.js,开始执行foo.js
- foo.js开始引入bar.js,进入bar.js
- bar.js中开始又引入了foo.js,这里产生了循环依赖,执行权并不会再交回foo.js,而是直接取其导出值,也就是module.exports。但是由于foo.js未执行完毕,导出值为默认的空对象,因此打印value of foo: {}
- bar.js执行完毕后,foo.js继续向下执行,打印出value of bar: this is bar.js。
# ES6 Module
// foo.js
import bar from './bar.js';
console.log('value of bar:', bar);
export default 'this is foo.js';
// bar.js
import foo from './foo.js';
console.log('value of foo:', foo);
export default 'this is bar.js';
// index.js
import foo from './foo.js';
// output
// value of foo: undefined
// value of bar: this is bar.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 非模块化文件
最常见的是在script标签中引入js。
比如jquery类库,直接是通过window.jquery绑定在全局对象下。无论是通过script标签引入,还是webpack打包引入,最终效果是一样的。
import './jquery.min.js';
1
# AMD
// 第一个参数是模块id,第二个是模块依赖,第三个是导出值,执行函数或者对象
define('getSum', ['calculator'], function(math) {
return function(a,b) {
console.log('xxx');
}
})
// 第一个参数是加载的模块,第二个参数是加载完成后执行的回调函数
require(['getSum'], function(getSum) {
getSum(2, 3);
})
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
AMD模块加载是非阻塞的。
# UMD
UMD是通用模块标准,它的目标是使模块能运行在各种环境下。
(function(global, main) {
if(typeof define === 'function' && define.amd) {
// AMD
define();
} else if (typeof exports === 'object') {
// CommonJS
module.exports = {};
} else {
// 非模块化环境
}
}(this, funtion(){}))
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11