一个简单的AMD模块加载器

一个简单的AMD模块加载器

参考

https://github.com/JsAaron/NodeJs-Demo/tree/master/require

PS Aaron大大的比我的完整

PS

这不是一个通用的模块加载器, 实际上只是require define 函数的实现

所以每个模块的js都要引入到html文件中

如何防止变量冲突呢?

实际上就是用函数把他们包起来

(function(exp){
var mod = {
name:'modA'
};
exp[mod.name] = mod;
})(exp);

exp是一个全局变量 保存所有的模块 然后require一个模块的时候实际上就是去exp这个全局变量中找

AMD是如何使用模块的呢

//定义一个模块
define('a', function(){
console.log('this is mod A');
return {
name: 'a'
}
}); //调用一个模块
require('a',function(a){
console.log(a.name)
}) require('a', function(a){
console.log(a.name + ' 2 ');
})

Step1


var require; //两个全局量
var define;
(function() {
var modules = {};
require = function(id, callback) {
if (!modules[id]) {
throw "module " + id + " not found";
}
if (callback) {
var module = build(modules[id]);
callback(module)
return module;
}
};
define = function(id, factory) { //模块名,模块本身
if (modules[id]) {
throw "module " + id + " 模块已存在!";
}
modules[id] = {
id: id,
factory: factory
};
}
//module {id: '' factory: function(){...}} //factory的function就是一个模块function中的内容
function build(module) {
var factory = module.factory,
id = module.id;
module.exports = factory() || {};
return module.exports; //模块return的结果
}
})();

那么这样一来 上面require的执行结果是

this is mod A
a
a.js:2 this is mod A
a 2

模块确实是加载了 但是模块A执行了两次

Step2

那么凡是require过的模块 下次再次require 应该从保存的地方取出来而不是再次执行factory

需要一个全局变量 pushStack = {} 来保存

  function build(module) {
var factory = module.factory,
id = module.id;
if(pushStack[id]){
return pushStack[id];
}
module.exports = factory() || {};
pushStack[id] = module.exports;
return module.exports; //模块return的结果
}

Step3 require增加多依赖支持

有了多依赖支持就可以这样做

require(['a', 'b'], function(a,b){
console.log(a.name);
console.log(b.name);
});

改造require就可以了

  require = function(id, callback) {
if(Object.prototype.toString.call(id) == '[object Array]'){
var ids = id, allMod = {};
ids.forEach(function(id){
allMod[id] = build(modules[id]);
});
callback.apply(null, allMod);
}else{
if (!modules[id]) {
throw "module " + id + " not found";
}
if (callback) {
var module = build(modules[id]);
callback(module)
return module;
}
}
};

Step4

有时候模块本身存在依赖 比如

define('c',['a','b'],function(a,b){
});

那么define的时候就要对参数做判断 如果是三个参数表示是有依赖的模块

var require; //两个全局量
var define;
(function() {
var modules = {},pushStack = {}; //require的时候才是真的执行模块
require = function(id, callback) {
if(Object.prototype.toString.call(id) == '[object Array]'){
var ids = id, allMods = [];
ids.forEach(function(id){
allMods.push(build(modules[id]));
});
callback.apply(null, allMods);
}else{
if (!modules[id]) {
throw "module " + id + " not found";
}
if (callback) {
var module = build(modules[id]);
callback(module)
return module;
}
}
}; //define的时候是把模块存放起来 存到modules[]中
//define的参数 若是2个 那么是 模块名,模块本身, 若有三个参数 则是 模块名,依赖列表,模块本身
define = function(id) {
var deps, factory;
if (modules[id]) {
throw "module " + id + " 模块已存在!";
}
if(arguments.length > 2){
deps = arguments[1];
factory = arguments[2];
modules[id] = {
id: id,
deps: deps,
factory: factory
};
}else{
factory = arguments[1];
modules[id] = {
id: id,
factory: factory
};
}
}
function build(module) {
var factory = module.factory,
id = module.id;
if(pushStack[id]){
return pushStack[id];
}
if(module.deps){
var deps = [];
module.deps.forEach(function(id){
deps.push(build(modules[id]));
});
module.exports = factory.apply(null,deps);
}else{
module.exports = factory() || {};
}
pushStack[id] = module.exports;
return module.exports; //模块return的结果
}
})();

如何加入CMD支持

完成上面,就可以通过下面的方式定义模块

define('a',function(){
}); define('c',['a','b'],function(a,b){
}); require('a','b',function(a,b){}})

在Seajs中CMD类似于这样

define('c', function(require, exports, module) {
var a = require('a')
var b = require('b');
});

实际上就是改造require (require的时候才是执行模块的定义函数)

require原本都是接受2个参数 依赖名以及一个函数 现在require可以只接受一个参数

var require; //两个全局量
var define;
(function() {
var modules = {},pushStack = {}; //require的时候才是真的执行模块
require = function(id, callback) {
if(Object.prototype.toString.call(id) == '[object Array]'){
var ids = id, allMods = [];
ids.forEach(function(id){
allMods.push(build(modules[id]));
});
callback.apply(null, allMods);
}else{
if (!modules[id]) {
throw "module " + id + " not found";
}
if (callback) {
var module = build(modules[id]);
callback(module);
return module;
}else{
return build(modules[id]);
}
}
}; //define的时候是把模块存放起来 存到modules[]中
//define的参数 若是2个 那么是 模块名,模块本身, 若有三个参数 则是 模块名,依赖列表,模块本身
define = function(id) {
var deps, factory;
if (modules[id]) {
throw "module " + id + " 模块已存在!";
}
if(arguments.length > 2){
deps = arguments[1];
factory = arguments[2];
modules[id] = {
id: id,
deps: deps,
factory: factory
};
}else{
factory = arguments[1];
modules[id] = {
id: id,
factory: factory
};
}
}
function build(module) {
var factory = module.factory,
id = module.id;
if(pushStack[id]){
return pushStack[id];
}
if(module.deps){
var deps = [];
module.deps.forEach(function(id){
deps.push(build(modules[id]));
});
module.exports = factory.apply(module,deps);
}else{
module.exports = factory(require, module.exports, module) || {};
}
pushStack[id] = module.exports;
return module.exports; //模块return的结果
}
})();

之后就可以这样通过CMD的方式引入模块了

define('c', function(require, exports, module) {
var a = require('a');
var b = require('b');
return {
name: 'c' + a.name + b.name
}
});
上一篇:golang文件相对路径问题


下一篇:通过Dapr实现一个简单的基于.net的微服务电商系统(十三)——istio+dapr构建多运行时服务网格之生产环境部署