fe ? not exsit ?

几种简易的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属性。

特点

  1. 运行在模块作用域
  2. 加载顺序即代码顺序
  3. 加载时运行一次然后缓存,再次加载取缓存模块
  4. 模块一旦输出即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

  1. exports 是指向的 module.exports 的引用。
  2. require() 返回的是 module.exports 而不是 exports
  3. 区分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执行流程

  1. 检查缓存文件,是否缓存之中有指定模块
  2. 如果缓存之中没有,就创建一个新的Module实例
  3. 将它保存到缓存
  4. 加载指定的模块文件,读取文件内容之后,编译执行文件代码
  5. 如果加载/解析过程报错,就从缓存删除该模块
  6. 返回该模块的 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真的不熟悉 === 不会,毕竟小鲜肉不是生活在远古时代。暂时没有兴趣。