一个简单的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
    }
});
posted @ 2015-11-10 17:03  cart55free99  阅读(307)  评论(0编辑  收藏  举报