来自本人博客:http://ilovespringna.com/typecho/index.php/archives/34350/
Cold的基本思路类似于kissy的seed,所有的代码绑定在一个基础性的全局对象Cold上,通过动态绑定的方式给这个Cold对象附加新的子对象,像一个种子一样不断成长。
如图,Cold的命名空间结构和目录结构是基本一致的,划分为core、component、util、task等层次。core是Cold的核心部分,提供了js的基本功能,包括有dom、event、ajax、anim等,每个js文件都定义了一类问题的解决方案。component为组件群,用于解决页面中遇到的实际问题,比如拖放组件,焦点图,图片的延迟载入等等,每个component都不同程度地依赖于core中的某些部分。task是以页面为粒度的JS物理文件集合,是建立在core和component上的页面应用的代码。util则是一些独立的工具方法,一些不涉及页面dom操作的工具类都放在这里,它们一般不依赖于上述任何层次。
对象Cold定义在cold.js中,cold.js提供了框架的基本信息(包括version、debug、cache等),具有loader的功能,并提供了ready方法。基本结构如下:
window['Cold'] = { version : '0.0.1', debug : true, scripts: { 'loadingNum' : 0, 'nodes' : {} }, //... add : function(namespace, req, doFunc){ //... }, load : function(namespace, callback){ //... }, ready : function(){ //... } };Cold中使用add方法定义模块,用load方法载入模块,一个loadingNum和nodes对象来管理模块的载入状态,最后用ready来检测模块是否已经成功附加到Cold对象上。
Cold.add(namespace, req, doFunc)
add函数是Cold架构中最重要的函数,被用在每个模块的定义中,它做了下面几件事: 1.接收开发者定义的模块的namespace,利用这个namespace将doFunc的返回值附加到Cold的相应位置。 2.检测模块的依赖模块,依次载入未载入的模块,并定义计数器给依赖模块,当最后一个依赖模块载入时,执行当前模块的定义代码(doFunc)。 3.执行每个模块载入后的回调函数,这个非常重要,待会具体说明。Cold.load(namespace, callback)
这个函数的思路来自于C/C++中的include,java/php中的import。加入你的页面需要动画效果和ajax组件,那么很简单:Cold.load('anim'); Cold.load('ajax');load方法会根据提供的namespace去寻找模块的物理位置。在这里,模块的命名空间,模块名,物理位置实际上是统一的,互相对应的。
Cold.loadingNum和Cold.nodes
这是两个状态寄存器。loadingNum表示当前正在载入的模块数,每一个Cold.load都会加一,载入成功后减一。当这个数为0时则表示所有这个页面需要用到的模块已经完全载入。 Cold.nodes对象以每个模块所代表的script节点为名,以当前的状态为值。而每个模块分为四个状态:
1.未load前为undefined。 2.执行load函数后,script节点建立,开始读取对应的js文件,此时状态为loading。 3.读取对应js文件完毕后,开始执行文件中add函数定义模块,此时状态为loaded。但是由于依赖模块的存在,这时add的定义函数doFunc可能尚未执行。 4.doFunc在所有依赖模块成功附加后执行,模块定义完成,状态为attached。
Cold.ready(func)
此函数在domReady时开始一个轮询,当loadingNum为0时开始执行ready中的代码。详述模块依赖的处理
在Cold里,由于模块之间的依赖关系是定义在每个模块的js文件的add函数里的,因此只有当一个模块的js文件载入后才能获知其依赖模块,比如动画组件anim.js://anim.js Cold.add('anim', ['dom'], function(){ //--[1]--anim定义代码 });它依赖于dom,当使用Cold.load('anim'),到执行add函数时,[1]处的代码不能立刻执行,因为它依赖于dom,[1]的代码使用了dom里面的内容。所以需要将[1]处的代码做一个延后,使其延后到dom.js中的定义代码执行后执行。 具体的方式是在dom.js的script节点上绑定一个callback属性,用于存储[1]的代码。
//dom.js Cold.add('dom', ['browser'], function(){ //--[2]--dom定义代码 }); //browser.js Cold.add('browser', function(){ //--[3]--browser定义代码 });
当执行dom.js后,同理,[2]的代码需要和[1]一起延后到browser.js里的定义代码之后进行执行。在browser.js的script节点上绑定一个callback属性,用于存储[2]以及dom.js的callback里存储的代码[1]。 当browser.js执行后,add函数会在执行完browser自身的定义代码[3]后,执行browser.js对应script节点里的callback属性(如图)。从而完成整个依赖调用[3]—[2]—[1]。
循环依赖(loaded状态在循环依赖中的作用)
reqList : { 'a' : ['b', 'c'] }上面代码表示a依赖于b、c。 思路如下: 当模块add的时候,将当前模块a以及当前模块a对应的reqList一起加到当前模块a的每个依赖模块所对应的reqList中去。即假若a依赖于b、c,则reqList[b] = reqList[c] = ['a'];若b依赖于d,则reqList[d] = ['b', 'a'],表示模块d依赖于模块b和a,如此递进。 而判断循环依赖的方式是:读取模块的依赖项,并到当前模块的reqList中去检查是否与依赖型重复,如果有,则判断出现循环依赖。比如:模块d的依赖于a、e,则将a、e到reqList[d]中去比对,出现重复项a,因此判断为循环依赖。 解决循环依赖的方式是将模块d的重复依赖项a删去。
小结
这里用了一些文字总结了Cold框架到目前为止的一些成果,在写的过程中本人也获益良多,接下来要继续完善和学习有关js框架的各类问题,欢迎交流共勉!