从头开始写框架(二):孕育框架的种子_中
上一篇我们介绍了AMD规范,异步模块的定义与加载,我们完成了定义的部分。接下来,我们来完成加载的部分。
我们之前已经可以用define来定义模块了,那么现在怎么去使用的?我们只是把定义好的模块存进了仓库,用的时候还需要一个方法来使用它。
让我们先来用define来定义一些模块:
define("a" , [] , function(){ //无依赖模块 return 1; }); define("b" , ["a"] , function(a){ //有一个依赖的模块 return ++a; }); define("c" , ["a","b"] , function(a,b){ //有两个依赖的模块 一会我们要使用这个模块 return a+b; });
这里定义了3个模块,其中有一个没有依赖,两个是存在依赖模块的模块。
然后我们需要一个方法来使用定义好的模块:(对于apply和call方法不懂的话,可以移步我的另一篇文章:来聊聊apply和call)
var noop = function(){}; //为apply方法准备的一个空函数; function use(name){ //参数:模块名 if(modules[name]){ // 如果模块定义过: var module = modules[name]; //创建一个副本 if(!module.entity){ //如果实例不存在,说明是第一次创建 var args = []; for(var i=0;i<module.dependencies.length;i++){ //遍历依赖列表 modules[module.dependencies[i]].entity ? //依赖模块的实例以存在的话 args.push(modules[module.dependencies[i]].entity) : //直接获取实例(缓存方法), args.push(this.use(module.dependencies[i])); //不然单独获取一次 }; module.entity = module.fn.apply(noop , args); //用apply方法为模块实例扩展 }; return module.entity; //以后调用可以直接调用缓存 }else{ throw new Error("Error:"+ name +" is not define"); // 如果没有定义模块,直接抛出一个错误. } };
这里我们定义了一个使用模块的方法,通过模块名来调用定义过的模块。而使用过一次的模块,我们把它存入缓存中,这样以后再次使用时,直接调用它的实例就可以了。
那么接下来我们来用这个方法来使用我们定义好的模块:
var d = use("c"); console.log(d)//输出3
这样我们就能使用任意我们定义过的模块了,而重新定义模块时,不会出现模块名相同导致模块被覆盖的情况。
那么我们怎么用这个方法来扩展我们的命名空间呢?显然,define和use方法是不能直接使用的,因为我们的模块是定义在了工厂方法的参数中,而这两个方法是被定义在了工厂方法内部中,所以我们是获取不到的。
那么让我们来改造一下这两个方法
这个出自Vuejs:
var moduleMap = {}; //模块仓库 function require(ID){ //参数为模块的名字,这里直接用数字来给模块定义id if(moduleMap[ID]) //如果模块已经存在 return moduleMap[ID].exports;//直接返回实例,并结束代码块 var module = moduleMap[ID] = { //声明一个新对象 exports:{},//初始化实例 id:ID, loaded:false//初始化加载状态 }; modules[ID].call(module.exports, module, module.exports, require); //用call方法将模块扩展到仓库中 module.loaded = true; //设置模块加载状态 return module.exports; //返回模块实例 }; return require(0); //返回第一个模块的调用
这个方法直接把定义模块的函数放到了工厂方法的参数里,简化了很多程序,调用也更方便了。那么接下来就剩下怎样将框架模块与方法注册到命名空间上了。
这部分我们放到下一篇来写。