演变历史
早期(全局函数模式)
Global 被污染,很容易命名冲突
function foo(){}
function bar(){}
1
2
中期(命名空间模式)
本质是对象,可以被操作修改,不安全
var DO = {
foo: function () { },
bar: function () { }
}
DO.foo()
1
2
3
4
5
后期(IIFE模式)
立即执行函数
(function ($) {
function foo() {
console.log($); // window 对象
}
function bar() { }
$.module = { foo: foo, bar: bar }
})(window)
module.foo()
1
2
3
4
5
6
7
8
问题:上面的几种方式尽管可以模块化,但是请求过多、依赖模糊、难以维护,还是达不到想要的目的,所以产生了四种模块化规范(AMD、CMD、CommonJS、ES6)
问题举例:
// js/module.js
(function ($) {
let fun = 'i am module'
function out() {
return fun
}
$.module = { out }
})(window)
// js/app.js
(function ($) {
let fun = 'i am app'
function out() {
console.log(fun, '————', module.out())
}
$.app = { out }
})(window, module)
// index.html
<script src="./js//module.js"></script>
<script src="./js/app.js"></script>
<script>
app.out() // i am app ———— i am module
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
模块化目的
降低复杂度
提升解耦性
避免命名冲突
部署方便,复用性强,按需加载
可维护性好
AMD
RequireJS 在推广过程中对模块定义的规范化产出
依赖前置,异步模块定义,在使用前需要先定义
专门用于浏览器端,模块的加载是异步的
基本语法:官方文档
暴露模块
// 定义有依赖的模块
// 数组内放置依赖的模块,回调内形参对应前面依赖的模块
define(['module1','module2'], function(m1, m2) {
'use strict';
// return 模块
});
// 定义没有依赖的模块
define(function() {
'use strict';
// return 模块
});
1
2
3
4
5
6
7
8
9
10
11
12
引入模块
require(['module1','module2'], function(m1, m2) {
// 使用 m1、m2
})
1
2
3
实例:
// js/libs/module1.js
// 定义有依赖的模块
define(['module2'], function (module2) {
let msg = 'module1'
function showMsg() {
console.log(msg, '————', module2.out());
}
// 暴露模块
return { showMsg }
});
// js/libs/module2.js
// 定义没有依赖的模块
define(function () {
let msg = 'module2'
function out() {
return msg
}
// 暴露模块
return { out }
});
// js/main.js
(function () {
requirejs.config({ // 配置模块
//By default load any module IDs from js/lib
baseUrl: 'js/', // 基本路径,出发点在根路径下
//except, if the module ID starts with "app",
//load it from the js/app directory. paths
//config is relative to the baseUrl, and
//never includes a ".js" extension since
//the paths config could be for a directory.
paths: { // 配置路径,注意尾部不要带 .js
// app: '../app'
module1: './libs/module1',
module2: './libs/module2',
}
})
requirejs(['module1'], function (module1) {
module1.showMsg()
})
})();
// app.html
<script data-main='js/main' src='js/libs/require.js'></script>
// data-main 用于指向主文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
CMD
SeaJS 在推广过程中对模块定义的规范化产出
依赖就近,同步模块定义,即用即返回
专门用于浏览器端,模块的加载是异步的
基本语法:官方文档
暴露模块
// 定义有依赖的模块
define(function (require, exports, module) {
// 引入依赖模块(同步)
var module2 = require('./module2')
// 引入依赖模块(异步)
require.async('./module3', function (m3) {
// m3 模块引入成功后在回调内直接使用
})
// 暴露模块
exports.xxx = value
})
// 定义没有依赖的模块
define(function (require, exports, module) {
// 暴露模块
exports.xxx = value
module.exports = value
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
引入模块
define(function (require) {
var m1 = require('./module1')
var m4 = require('./module4')
m1.show()
m4.show()
})
1
2
3
4
5
6
实例:
// js/libs/module1.js
// 定义有依赖的模块
define(function (require, exports, module) {
// 异步引入
require.async('./module2', function (m2) {
console.log(m2.out());
})
// 同步引入
let module2 = require('./module2')
let msg = 'module1'
function showMsg() {
console.log(msg, '————', module2.out());
}
// 暴露模块
module.exports = showMsg
});
// js/libs/module2.js
// 定义没有依赖的模块
define(function (require, exports, module) {
let msg = 'module2'
function out() {
return msg
}
module.exports = { out }
});
// js/main.js
define(function (require) {
let module1 = require('./libs/module1')
module1() // module2 / module1 ———— module2
})
// app.html
<script src='js/libs/sea.js'></script>
<script>
seajs.use('./js/main.js')
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
CommonJS
每个文件都可当做一个模块
在服务器端:模块的加载是运行时同步加载的
在浏览器端:模块需要提前编译(Browserify)打包处理(在浏览器端推荐 ES6 模块化,因为同步等待时间长,性能差)
基本语法:更多
// 模块暴露
// 本质暴露的是 exports 对象(初始 exports 为 {})
module.exports = value
exports.xxx = value
console.log(exports === module.exports) // true
// 引入模块
require(xxx) // 第三方模块直接引入,自定义模块按路径引入
1
2
3
4
5
6
7
8
ES6
基本语法:更多
// 暴露模块
export default value
export let a = { name: 'foo' }
export let b = { name: 'bar' }
// 引入模块
import value from '../path1'
import { a, b } from './path2'
1
2
3
4
5
6
7
8
AMD 与 CMD 差异
AMD 是 RequireJS 在推广过程中对模块定义提出的概念。
CMD 是 SeaJS 在推广过程中对模块定义提出的概念。
RequireJS 和 Sea.js 都是模块加载器,倡导模块化开发理念,核心价值是让 JavaScript 的模块化开发变得简单自然。
不同之处,两者的主要区别如下:
定位有差异。RequireJS 想成为浏览器端的模块加载器,同时也想成为 Rhino / Node 等环境的模块加载器。Sea.js 则专注于 Web 浏览器端,同时通过 Node 扩展的方式可以很方便跑在 Node 环境中。
遵循的规范不同。RequireJS 遵循 AMD(异步模块定义)规范,Sea.js 遵循 CMD (通用模块定义)规范。规范的不同,导致了两者 API 不同。Sea.js 更贴近 CommonJS Modules/1.1 和 Node Modules 规范。
CMD 推崇依赖就近,AMD 推崇依赖前置。
推广理念有差异。RequireJS 在尝试让第三方类库修改自身来支持 RequireJS,目前只有少数社区采纳。Sea.js 不强推,采用自主封装的方式来“海纳百川”,目前已有较成熟的封装策略。
对开发调试的支持有差异。Sea.js 非常关注代码的开发调试,有 nocache、debug 等用于调试的插件。RequireJS 无这方面的明显支持。
插件机制不同。RequireJS 采取的是在源码中预留接口的形式,插件类型比较单一。Sea.js 采取的是通用事件机制,插件类型更丰富。
————————————————
版权声明:本文为CSDN博主「肖ZE」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/lucky541788/java/article/details/88967378