# CommonJS
node中使用的就是commonjs规范
四个环境变量:module、exports、require、global
实际使用时,用module.exports定义当前模块对外输出的接口(不推荐直接使用exports),用require加载模块。
commonjs用同步的方式加载模块,在服务端,模块文件够存储在本地磁盘,读取会非常快。所以这样做不会有问题。但是在浏览器端,限制于网络等原因,更合理的加载方式应该是异步加载。
commonjs规范:
- 一个文件就是一个模块,拥有单独的作用域;
- 普通方式定义的变量、函数、对象属于该模块内;
- 通过require来加载模块;
- 通过module.exports或者exports来暴露模块中的内容;
注意:
- 当module.exports和exports同时存在的时候,module.exports会覆盖exports;
- 当模块内全都是exports是,其实就等同于module.exports;
- exports其实就是module.exports的子集;
- 所有代码都是运行在模块作用域,不会污染全局作用域;
- 模块可以被多次加载,但是只是会在第一次运行的时候加载。然后运行结果就会被缓存了。以后的加载就直接读取缓存的结果;
- 模块加载的顺序是按照代码出现的顺序同步加载的;
- __dirname代表当前模块所在的文件路径
- __filename代表当前模块文件所在的文件路径+文件名
# AMD
Asynchronous Module Definition,异步加载模块
他是在一个在浏览器端模块化的开发规范。不是原生js的规范。使用AMD规范进行页面需求开发,需要用到对应的函数库,require.js。
AMD采用异步加载的模块的方式。模块的加载并不会影响到后面语句的执行。所有以来这个模块的语句都定义在一个回掉函数中,等到加载完成之后,这个回掉函数才会执行。
使用require.js来实现AMD规范的模块化:用require.config()指定引用路径。用define()来定义模块用require来加载模块。
require主要解决的问题:
- 文件可以有依赖关系,被依赖的文件需要早于依赖它的文件加载的浏览器
- js加载的时候浏览会停滞页面的渲染,加载文件越多,页面响应的时间就会越长
- 异步加载前置
语法: difine(id, dependencies, factory)
- id:可选参数,用来定义模块的标识,如果没有提供该参数,脚本文件名(去掉拓展名)
- dependencies:是一个当前模块要用的模块名称组成的数组
- factory:工厂方法,模块初始化要执行的函数,或者对象,如果改为函数,他应该只被执行一次,如果是对象,此对象应该为模块的输出值。
案例:
amd
├── demo.html
├── js
│ ├── mod1.js
│ └── mod2.js
└── main.js
2
3
4
5
6
mod1.js
define(function() {
return {
a:3
}
})
2
3
4
5
mod2.js
define(['mod1'], function(m1) {
var a, b=2,c=3;
a=c * m1.a; //9
return {
a:a,
b:b
}
})
2
3
4
5
6
7
8
require.config({
paths: {
"mod1": "js/mod1",
"mod2": "js/mod2"
}
});
// or 配置baseUrl
// require.config({
// baseUrl: "./js",
// paths: {
// "mod1": "mod1",
// "mod2": "mod2",
// }
// });
require(['mod2'], function(m2) {
alert(m2)
})
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
在demo.html中引入,会输出 {a: 9,b: 2}
<script src="https://cdn.bootcdn.net/ajax/libs/require.js/2.3.6/require.js"></script>
<script src="main.js"></script>
2
# CMD
CMD是另一种模块化方案,它和AMD很类似,不同点在于:
AMD推崇依赖前置,提前执行,而CMD推崇依赖就近,延迟执行,这个规范其实就是sea.js推广过程产生的。
因为CMD推崇一个文件一个模块。因此经常会用文件名作为模块id,CMD推崇依赖就近,
所以一般不在define的参数中写依赖,而是在factory中写:define(id,deps,factory)
factory: function(require,exports,module) {}
- require:factory的第一个参数,用来获取其他模块提供的接口
- exports:一个对象,用来向外提供模块接口
- module:一个对象,上面存储了与当前模块相关联的一些属性和方法
cmd
├── asyncMod2.js
├── cmd.html
├── main.js
└── mod1.js
2
3
4
5
asyncMod2.js
define(function(require, exports, module) {
exports.mod2 = function() {
return "mod2"
};
});
2
3
4
5
6
7
mod1.js
define(function(require, exports, module) {
require.async('./asyncMod2.js', function(m2) {
console.log(m2.mod2())
})
exports.sayHello = function() {
return "hello"
};
});
2
3
4
5
6
7
8
main.js
seajs.config({
});
seajs.use(['./mod1'], function(hello) {
console.log(hello.sayHello(),$)
});
2
3
4
5
6
cmd.html中引入,输出 hello 与 mod2
<script src="https://cdn.bootcdn.net/ajax/libs/seajs/3.0.3/runtime.js"></script>
<script src="main.js"></script>
2
# UMD
一个整合了commonJS和AMD规范的方法。希望能解决跨平台模块的解决方案。
运行的原理:
UMD会先判断是否支持Node.js的模块(export)是不是存在。存在则使用node.js的模块方式。
再判断是不是支持AMD(define是不是存在。存在则使用AMD方式加载模块。
(function(window, factory){
if (typeof exports === 'object') {
module.exports = factory()
} else if (typeof define === 'function' && define.amd) {
define(factory)
} else {
window.eventUtil = factory()
}
})(this,function () {
// do something
})
2
3
4
5
6
7
8
9
10
11
# ES6Module
ES6在语言标准的层面上,实现了模块化功能,而且实现的相当简单,旨在成为浏览器和服务器通用的模块化解决方案,其模块化功能主要由俩个命令构成:exports和import,export命令由于规定模块的对外接口,import命令用于输入其他模块的功能。其实ES6还提供了export default的命令。为模块指定默认输出。对应的import语句不需要大括号。这也更接近AMD的引用写法。
ES6模块不是对象,import命令被JavaScript引擎静态分析,在编译的时候就引入模块代码。而不是在代码运行时加载,所以无法实现条件加载。也就使得静态分析成为可能。
(1)export
(2)import
import { fn } from './xxx' // export导出的方式
import fn from 'xx' // export default方式
2
3
export可以导出的是对象中包含多个属性、方法,export default只能导出一个可以不具名的函数。我们可以输用import引入。同时我们也可以直接使用require使用,原因是webpack启用了server相关。
# 总结
commonJS是同步加载的,主要是node.js也就是服务端也应用的模块化机制。通过module.exports导出声明。通过require('')加载。每个文件都是一个模块。他有自己的独立的作用域。文件内的变量,属性函数等不能被外界访问,node会将模块缓存,在第二次加载的时候会从缓存中获取。
AMD是异步加载的,主要是在浏览器环境下应用,require.js是遵守AMD规范的模块化工具。它通过define()定义声明,通过require('',function() {})来加载。
ES6模块化加载时,通过exports default导出。用import来导入,可以通过对导出内容进行解构。ES6模块运行机制与commonjs运行机制不一样。js引擎对脚本静态分析的时候,遇到模块加载指令后会生成一个只读引用。等到脚本真正执行的时候。才会通过引用模块中获取值,在引用到执行的过程中,模块中的值发生变化,导入的这里也会跟着发生变化。ES6模块是动态引入的。并不会缓存值。模块里总是绑定其所在的模块。