一种比seajs、requirejs简洁n倍的模块加载和管理组件:modulejs

一种比seajs、requirejs简洁n倍的模块加载和管理组件:modulejs。 
他是虚拟同学经过半年痛苦的选择以及对seajs纠结的使用后,下定决心,潜心分析各种类似库后,自己实现出来的。modulejs是一款比seajs、commonjs更加简洁、小巧的javascript模块化开发管理工具。  思路更加精巧优雅,包含注释在内只有222行,同时也吸收了seajs和requirejs的一些优点。 

see:https://github.com/eccued/modulejs 

/**
 * modulejs 1.0.2
 * author:kpxu\jimyan\wowowang
 * description:
 *   modulejs是一款比seajs、commonjs更加简洁、小巧的javascript模块化开发管理工具。
 *   思路更加精巧优雅,包含注释在内只有222行,同时也吸收了seajs和requirejs的一些优点
 * see:https://github.com/eccued/modulejs
 */
var modulejs, require, define;
(function(global) {
  var mod, cfg, _modulejs, _define, _require;
  var version = "1.0.2";
  cfg = {
    debug: false, //调试模式。
    alisa: {}, //模块的所在文件路径定义
    vars:{},  //变量模块名
    uris: {}, //加载文件列表 文件URL作为下标,true为已加载  false为未加载
    modules: {}, //模块列表 模块id作为下标
    callback: [], //入口回调方法的数组,所有modulejs所定义的方法都压入这个数组
    deps: {}, //记录运行中需要用到的依赖队列
    events: {} //消息队列
  };
  //读取预配置
  _modulejs.config = config;
  config(global["_moduleConfig"] ? global._moduleConfig : cfg);
  global["_moduleConfig"] = cfg;
  //输出api
  require = _require; //引用
  define = _define; //定义
  modulejs = _modulejs; //入口
  //监听模块准备就绪的事件,并检查需要回调module的相关依赖是否全部就绪
  on("module_ready", function(id) {
    var init = cfg.callback = cleanArray(cfg.callback);
    //顺序扫描所有的回调方法,检查要回调的module是否完成依赖加载
    for (var i = 0; i < init.length; i++) {
      //如果依赖满足则开始执行并删除回调
      if (init[i] && checkDeps(init[i].dependencies)) {
        var cb = init[i].factory;
        var deps = init[i].dependencies;
        var mods = []; //把模块的相关依赖,依次作为参数传入
        for (var j = 0; j < deps.length; j++) {
          mods.push(_require(deps[j]));
        }
        init[i] = null;
        cb.apply(null, mods);
        cfg.debug && emit("callback_is_run",cb.toString().replace(/[\r\n]/g,""));
      }
    }
  });

  //模块定义api,有三个参数的时候第2个参数为依赖array,2个参数时,第2个为回调

  function _define(id, deps, factory) {
    //只有两个参数的时候,表示忽略了deps
    if (arguments.length === 2) {
      factory = deps;
      deps = null;
    }
    //保证deps一定是个array
    deps = isType("Array", deps) ? deps : (deps ? [deps] : []);
    //静态分析内容中的依赖
    if (isType("Function", factory)) {
      var _deps = parseDependencies(factory.toString());
    }
    //合并明文依赖和分析依赖
    deps=mergeArray(deps,_deps);
    //构造一个model压入mod仓库
    var mod = new Module(id);
    mod.dependencies = deps || [];
    mod.factory = factory;
    //如果是_init的话,则放入回调模块数组,否则放入module序列
    if (id == "_init") {
      cfg.callback.push(mod);
    } else {
      //非_init则为普通模块,放入module序列
      cfg.modules[id] = mod;
    }
    //广播消息处理
    emit("module_ready", id);
  }
  //模块实例化或返回实例

  function _require(id) {
    id=parseVars(id);//解析变量依赖
    var module = cfg.modules[id];
    // 如果module不存在则返回null
    if (!module) {
      return null
    }
    //如果module的exports不为null,则说明已经实例化了。
    if (module.exports) {
      return module.exports;
    }
    //走到这里则说明module还没有被实例化
    var factory = module.factory;
    //判断factory类型,function则执行,obj的直接返回
    //为function的时候将被直接执行,执行时将会接收到3个参数factory(require,exports,module)
    var exports = isType("Function", factory) ? factory(require, module.exports = {}, module) : factory;
    module.exports = exports === undefined ? module.exports : exports;
    return module.exports; //require一个模块的结果是返回该module的exports
  }
  //入口方法

  function _modulejs(deps, factory) {
    //把入口回调作为一个回调模块定义,当有多个入口的时候,都放在数组里面
    _define("_init", deps, factory);
    //加载入口回调模块的依赖方法,在模块检测过程中进行依赖加载
    emit("module_ready", "_init");
  }
  //递归检查深层依赖环境是否完成,并加载确实的module

  function checkDeps(deps) {
    var list = cfg.deps;
    //重置依赖分析数组
    for (var i in list) list[i] = 1;
    //构造当前依赖的递归依赖
    getDesps(deps, list);
    //依次检查依赖,发现有module遗失则返回flase。进入加载流程
    for (var i in list) {
      //检查该依赖模块是否遗失,如果遗失则中断检查去加载
      if (!cfg.modules[i]) {
        loadModule(i);
        return false;
      }
    }
    return true;
    //获取深层依赖关系队列

    function getDesps(deps, list) {
      //循环检查新的依赖队列
      for (var i = 0; i < deps.length; i++) {
        //如果该module未在全局依赖队列中,则加入到全局队列中
        if (!list[deps[i]]) {
          list[deps[i]] = 1;
        }
        //如果该module存在,则递归构造该module的依赖队列。
        //凡是构造过的module就把状态设置为2,防止循环依赖
        if (cfg.modules[deps[i]] && list[deps[i]] != 2) {
          list[deps[i]] = 2;
          getDesps(cfg.modules[deps[i]].dependencies, list);
        }
      }
    }
  }
  //解析变量模块名
  function parseVars(id) {
    var VARS_RE = /{([^{]+)}/g
    var vars = cfg.vars
    if (vars && id.indexOf("{") > -1) {
      id = id.replace(VARS_RE, function(m, key) {
        return isType("String",vars[key]) ? vars[key] : m
      })
    }
    return id
  }
  //配置方法

  function config(obj) {
    for (var k in obj) {
      cfg[k] = obj[k];
    }
    return mod;
  }
  //module原型

  function Module(id) {
    this.id = id;
    this.dependencies = [];
    this.exports = null;
    this.uri = "";
  }
  //加载模块的对应文件

  function loadModule(id) {
    //如果module存在则直接返回
    if (cfg.modules[id]) {
      emit("module_ready", id);
      return;
    }
    //查找module所对应的文件
    //todo:这里可以考虑支持把没有alisa配置的module按照某些规则生成一条url进行加载。
    var url = cfg.alisa[id] ? cfg.alisa[id] : "";
    if (!url) {
      emit("module_loss_alisa", id);
      return;
    }
    //检查是否已经加载该文件,如果文件已经在加载队列,则返回,等待后续代码的执行
    if (cfg.uris[url]) {
      return;
    }
    //开始加载
    cfg.uris[url] = 1;
    var head = document.getElementsByTagName("head")[0] || document.documentElement;
    var baseElement = head.getElementsByTagName("base")[0];
    var node = document.createElement("script");
    node.charset = "utf-8";
    node.async = true;
    node.src = url;
    //todo:这里可以考虑监控一下文件加载失败的情况,方便报错和监控等后续的健壮性功能
    baseElement ? head.insertBefore(node, baseElement) : head.appendChild(node);
    cfg.debug && emit("file_loading", url);
  }



  //事件监听

  function on(name, cb) {
    var cbs = cfg.events[name];
    if (!cbs) {
      cbs = cfg.events[name] = [];
    }
    cbs.push(cb);
  }

  //事件广播

  function emit(name, evt) {
    cfg.debug && console.log(name, evt);
    for (var i in cfg.events[name]) {
      cfg.events[name][i](evt);
    }
    if (name === 'error') {
      delete cfg.events[name];
    }
  }
  //清理数组中的空元素

  function cleanArray(a) {
    var n = [];
    for (var i = 0; i < a.length; i++) {
      a[i] && n.push(a[i]);
    }
    return a;
  }
  //去重合并数组
  function mergeArray(a,b){
    for (var i = 0; i < b.length; i++) {
      (("," + a + ",").indexOf("," + b[i] + ",") < 0) && a.push(b[i]);
    }
    return a;
  }
  //对象类型判断

  function isType(type, obj) {
    return Object.prototype.toString.call(obj) === "[object " + type + "]"
  }
  //分析module的依赖关系

  function parseDependencies(code) {
    var commentRegExp = /(\/\*([\s\S]*?)\*\/|([^:]|^)\/\/(.*)$)/mg;
    var cjsRequireRegExp = /[^.]\s*require\s*\(\s*["']([^'"\s]+)["']\s*\)/g;
    var ret = [];
    code.replace(commentRegExp, "").replace(cjsRequireRegExp, function(match, dep) {
      dep && ret.push(parseVars(dep));
    })
    return ret;
  }
}(this));


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值