kissyLite的包管理和无需预先注册的带依赖关系模块异步加载
现在很多同学们都在代码里模拟YUI3实现JavaScript异步模块化加载,也就是一个add,一个use..
YUI3里对模块的加载是串行模式,当没有combo配合时,看起来很不美观.
今天army8735同学提到这个事儿,发现还真是很多同学注意到了这一点.kissy本身的add use就是并行,也有同学对YUI3做了改进.
而进来我正在开发kissylite,是kissy的一个支持有限方法的子集,目标是用1.5k代码支持包管理和模块化管理,也遇到了这个问题.不如拿出来一起讨论.
我觉得kissy和yui3的问题是模块必须先注册,这样扩展性有一定的局限.
kissylite引入了包的概念,每一个包对应一个codebase,通过模块名就完全确定js所在路径.
那么在一个包内新增了模块之后,种子文件是不需要修改即可直接使用的.
这样增加循环依赖的风险a->b b->c c->a..
虽然这样循环了明显是程序逻辑错误,但是如果不加以防范,合作开发,多次上线,死循环漏到线上那打击是致命的..
我的解决办法很土..在env中有一个对象记录x=模块依赖哪些,又支持哪些..当出现x依赖x的时候就抛一个error出来好了.
以a->b b->c c->a为例use("a")时:
首先载入a,Env中:
依赖关系方面a->b,
支持关系方面b->a
然后载入b,Env中:
依赖关系方面:b->c,在通过上面的支持关系可以得到a同样依赖c,即a->b,c
支持关系方面:c->b;a,b->a
最后载入c,Env中:
依赖关系方面:c->a,再通过现有的支持关系能得到a->b,c,a 矛盾出现a依赖自己,报个错吧:循环依赖.
这种算法效率是很低的,但是先粗暴的实现一下用着,每次add可能都要访问相关依赖关系对象和支持关系对象,毕竟kissylite目标是给页面中的广告,这类小应用的,模块不会太多.算抛砖吧,求更好的算法.
另一方面这里看到a b c的加载是串行的,在开发完成后,每次use可以自动给出一个use的并行优化方案.
比如use("a"),在use成功后,可以控制台log一下,为了更好的并行加载模块,你应该use("a,b,c")..这样就可以边开发,边优化.
kissylite还解决了一个问题,就是add的时候永远不attach.
steve blog:Evolution of Script Loading给出的mobile上js加载方案,看到代码从按需加载进化到了按需执行的阶段.
对于模块化js简单一些,只要不先attach,只是一个单纯简单的add,耗费的精力少的多了.
大家可能担心use时候attach是不是慢了些,这个同样可以边开发边优化.
在开发一个页面,可以把常用的功能都跑一边,Env中包含你所有使用过的包.
那么就可以在domready后,不忙的时候把这些先use一遍...
这就有点facebook primer的意思了,domready之前按需执行,domready之后选择性预热.
这里也说说异步执行单元的串行和并行.这个老道正好在velocity china提到.
异步执行单元:asyncUnit(args,callback).所有可能需要等待一段时间才能收到结果的函数都可以叫异步单元.比如getScript(url,callback),use(mods,callback),还有一些动画回调,都是异步执行单元.
我们常常遇到很多异步执行单元,比如use(mods)这个场景.
按程序的业务逻辑,如果这些异步单元需要串行,则会遇到多层复杂的callback嵌套.
a(a1,function(resA){ window.resA = resA if(window.resB){ c(); }});b(b1,function(resB){ window.resB = resB if(window.resA){ c(); }})function c(){ //your code run after resA & resB returend }