几种简易的module理解及应用
入行来就已经是esm时代怎么破?很多代码支持AMD,CMD,只知晓是解决当时的痛点而产生加载模块的产物,并不了解具体的实例。记录学习一下。
global-module
一、NAMESPACE + IIFE + INJECTION
不得不说,现在的代码中还有小部分遗留形式吧。与其讲是一种上古module,不如讲是一种模块设计模式的体现吧。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
(function($) {
var MAIN = {
init: function() {
// TODO
var self = this;
$.load( function() {
this.loadScript();
});
},
loadScript: function() {
// TODO
}
};
MAIN.init();
})(JQuery);
// 控制pravite与public,形成内外部的访问权限范围
var module = (function(){
var secretData = 'i am secret';
return (function() {
console.log(secretData);
})();
});
module();
首先采用NAMESPACE的形式把init与loadScript封装在MAIN, 减少全局变量的数量。再采用IIFE匿名函数的形式自执行,形成内部的封闭区域避免侵入造成污染。在用Jquery传参的形式添加依赖。MAIN中init函数就可以调用jQuery的load函数。
二、突然想到的jq.conflict
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
// 一个小demo
(function(root) {
function $() {
console.log('jQuery $ ://TODO');
}
root.$ = $;
$.conflict = function () {
root.$ = null;
return $;
}
})(global);
$();
const jQuery = $.conflict();
function a () {
return jQuery;
}
// if have confilct ?
(function(root) {
function Zepto() {
console.log('Zepto://TODO');
}
root.$ = Zepto
})(global);
$();
a();
SCRIPT LODER
1
2
3
4
5
6
7
<body>
script(src="A")
script(src="B")
script(src="C")
...
script(src="Z")
</body>
以一个串行顺序加载,执行顺序为dom顺序,其中后续js文件可能依赖前置js文件。带来更多问题依赖模糊、维护复杂、代码文件繁多。啊啊啊啊啊。去死吧。
COMMONJS
为服务端优化的模块格式。最典型的实现模型就是Node大法。也就是常见的require加载和module暴露的应用。变量module代表当前模块,该变量是一个对象。module.exports属性是对外的接口。加载A模块就是加载A模块的exports属性。
特点
- 运行在模块作用域
- 加载顺序即代码顺序
- 加载时运行一次然后缓存,再次加载取缓存模块
模块一旦输出即export出一个值,在模块内部改变值即不会改变输出值
1
2
3
4
5
6
7
8
9
10
11// a.js
var a = 1;
function add() {
a++;
console.log(a);
}
module.exports = { a: a, add: add}
// b.js
var test = require('a.js');
console.log(a) //2
test.add() //2
exports && module.exports
- exports 是指向的 module.exports 的引用。
- require() 返回的是 module.exports 而不是 exports
区分es2015的export && export default && import
1
2
3
4
5
6
7
8
9
10// 第一点 引用关系。exports不能赋值一个值,因为这样等于切断了exports与module.exports的联系。
// a.js
exports.word = 'hello'
module.exports = function () {
console.log(exports.word)
}
// b.js
const fail = require('a.js')
fail() // hello
console.log(fail.word) // undefined
require执行流程
- 检查缓存文件,是否缓存之中有指定模块
- 如果缓存之中没有,就创建一个新的Module实例
- 将它保存到缓存
- 加载指定的模块文件,读取文件内容之后,编译执行文件代码
- 如果加载/解析过程报错,就从缓存删除该模块
返回该模块的 module.exports
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// 证明一下 exports不能赋值一个值,因为这样等于切断了exports与module.exports的联系。
// require返回该模块的 module.exports
// a.js
exports.word = 'hello'
module.exports = function () {
console.log(exports.word)
}
// b.js
const fs = require('fs')
const path = require('path')
function runner(file) {
const code = fs.readFileSync(path.join(__dirname, file), 'utf-8')
const module = { exports: {} }
const fn = new Function('module', 'exports', code)
fn(module, module.exports)
return module.exports
}
const fail = runner('a.js')
fail()
console.log(`fail.word`, fail.word)
/**
* fn 到底生成了怎样的函数?
*/
function _fn(module, exports) {
exports.word = 'hello'
module.exports = function() {
console.log(exports.word)
}
}
AMD
// TODO
CMD
//TODO
提示
像更早的LABjs、YUI3 Loader真的不熟悉 === 不会,毕竟小鲜肉不是生活在远古时代。暂时没有兴趣。