# 第2章 模块打包

# CommonJS

CommonJS在每个模块首部默认添加了以下代码:

var module = {
    exports: {}
}
var exports = module.exports;
1
2
3
4
//test.js
var name = '1';

// index.js
require('./test.js');
1
2
3
4
5

test.js会形成一个属于模块自身的作用域,所有变量及函数外部不可见。

exports.name = 2;
module.exports = {
    name: 1
}
1
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
export default {
    name: 'test'
}
export default 'xxxx';
export default function () {};
1
2
3
4
5
import { default as myModule } from './test.js'
1

# CommonJS 和 ES6 Module 区别

# 动态与静态

CommonJS是动态的,模块依赖关系的建立发生在代码运行阶段。

ES6 Module是静态的,模块依赖关系的建立发生在代码编译阶段。

# ES6 Module优势

  1. 死代码检测和排除。
  2. 模块变量类型检查。
  3. 编译器优化。

# 值拷贝与动态映射

导入模块时,对于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

执行顺序:

  1. index.js中导入foo.js,开始执行foo.js
  2. foo.js开始引入bar.js,进入bar.js
  3. bar.js中开始又引入了foo.js,这里产生了循环依赖,执行权并不会再交回foo.js,而是直接取其导出值,也就是module.exports。但是由于foo.js未执行完毕,导出值为默认的空对象,因此打印value of foo: {}
  4. 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

# 非模块化文件

最常见的是在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

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