我的模块加载系统

本版本的重要更新是完全实现AMD规范。整个框架根据此新加载器重写,因此可以方便调用老外用AMD规范写好的JS库了.

  • 日志打印可以通过设置$.core.level排除某些不重要的日志打印。
  • 添加config方法来设置框架的一些重要信息或对模块进行别名。
  • 在VS系列实现智能提示。

有关本模块加载系统的使用,可以到这里详看教程.新加载器是同时支持AMD与玉伯搞的seajs的CMD.如果你在模块定义中的回调带有"require","exports","modules"中的任两个,加载器就视为CMD.不过无视是AMD与CMD,函数里面require, modules, exports都是可用的,完全可以像node.js这样加载模块.

有关require方法的模块标识,可以见这里

有关为什么要用AMD来管理我们的脚本,我想做过一些大项目的人应该心中有数,这不是一个合并JS所能取替的,详见此文


下面揭载我的加载器的运作机理,姑且从一个没有依赖的模块着手,比如

$.require( "$lang_fix" , function (){
     console.log( "xxxxxxxxxxxxxx" )
})

不用说,进入$.require(希望大家看这东西时,打开源码对照着看。)

String(list).replace( $.rword, function (el){
 
   })
 
el  = "$lang_fix"

进入Module._resolveFilename

url = $.core.base + el + ".js"

初次加载,肯定没有在modules中注册, modules[url]为undefined

于是进入 loadJS( url, id );

  • 创建一个iframe,
  • 在第一个script节点 用nick, Ns, nick分别保存url, $, innerDefine
  • 在第二个script节点 用url去加载目标节点

第二个节点视情况不同分别绑定onreadystatechange, onload, onerror

值得注意的是我们在注册模块时state还是为undefined,它会在innerDefine中修改state。

然后我们通过iframe中的script加载模块,而模块一般是这样的格式
define( "lang_fix" , function ( ){
   //==========略============
})

这里的define实质上是innerDefine

innerDefine里面做了几个很重要的事情(我们现在只需看第一,第二)

  1. 第一个事情把lang_fix这第一个参数换掉,换成nick, nick就是加载它的那个script的src。
  2. 第二个事情是将它对应的模块的状态改为1,也是modules[url].state = 1。
  3. 第三个事情是$, exports, require, module等对象强塞进模块工厂
  4. 第四个事情是转交真正的$.define去处理

在第二事情中,我们将模块的状态修改了,于是节点执行onreadystatechange/onload时

if (/loaded|complete|undefined/i.test( this .readyState) }{
     Ns._checkDeps();
      Ns._checkFail(self.document, nick);
}

Ns._checkFail发挥效力

_checkFail : function (  doc, id, error ){
     doc && (doc.ok = 1);
     if ( error || !modules[ id ].state ){
          this .log( "Failed to load [[ " +id+ " ]]" +modules[ id ].state);
     }
},

如果是死链,那无法调用define函数,也就无法调用innerDefine,状态为undefine

!modules[ id ].state == true,于是打印错误日志,当然以后我们讨论一下,是不是该throw

如果在旧式opera,它是会无法进入onreadystatechange, onload, onerror任一回调

在_checkFail中,我们会修复doc.ok = 1,那么在iframe中onload中我们检测doc.ok不等于1时,

就在_checkFail传入第三个参数true,让它打印错误日志

如果在FF,chrome, IE9,它们就会进入onerror回调,那里的调用代码中

Ns._checkFail(self.document, nick, true )

因此也顺利检测到死链。

反正只要加载失败,我们就立即把对应iframe移出DOM树!

好了,如果成功加载,我们就通过innerDefine到达$.define,并也把对应iframe移出DOM树

$.define干了如下几个事情

  1. 第一个事情,检测第二个参数是否为布尔,是说明其是补丁模块,如果布尔值为true,说明这个补丁模块对这个浏览器是没有用的,直接return,不执行它的模块工厂了。如果为false,比如IE6,补丁模块对它总是有用,先去掉此参数,继续往下执行。
  2. 第二个事情,检测参数个数,如果只有两个,说明只有模块名与模块的回调(亦有可能不是回调),那么我们插入一个空数组作为依赖列表
  3. 第三个就是检测第三个参数是否为函数,不是函数,比如说是个对象,我们要将它塞入一个函数

比如

define({
   aaa:2
})

经上面几次转换,依次变为

define( "http://xxxxxxx/aa.js" ,{
   aaa:2
});
//---->
define( "http://xxxxxxx/aa.js"  ,[],{
   aaa:2
});
 
//---->
define( "http://xxxxxxx/aa.js"  ,[], function (){
  return  {
     aaa:2
   }
});
 
//---->
$.require([], function (){
  return  {
     aaa:2
   }

最后我们将参数的顺序重排一下,再次调用$.require

//0,1,2 --> 1,2,0
this .require( args[1], args[2], parent );

由于lang_fix是没有依赖的,因此dn === cn 相当于 0 == 0,执行install( id, args, factory );

install简而言之是将模块工厂执行,将state改为2。

我们还需要注意一下,我们每次调用$.require或加载一个脚本时都执行_checkDeps方法。

当lang_fix的状态改为2后

$.require( "$lang_fix" , function (){
      console.log( "xxxxxxxxxxxxxx" )
})

这个回调也将执行!

控制台打印xxxxxxxxxxxxxxxx

源码位于这里

 
 
标签:  javascriptmass

你可能感兴趣的:(JavaScript,mass)