yui3 的沙箱机制可以在多人协作开发方面提供很大的便利,常被举的例子之一就是:
重复载入脚本问题:
...
//开发者 A
<script src="a.js"></>
<div>
</div>
<script>
//a组件应用
</>
....
//开发者 B
<script src="a.js"></>
<script src="b.js"></>
<div>
</div>
<script>
//a,b组件一起应用
</>
开发者A,B的分离造成了完全没必要的 a 脚本引入,不过好点的是浏览器会缓存,但是更好的解决方案则是a完全不必引入。
yui3 的解决方案:
...
//开发者 A
<div>
</div>
<script>
//a组件应用
YUI().use("a",function(){
//action 1
});
</>
....
//开发者 B
<div>
</div>
<script>
//a,b组件一起应用
YUI().use("a","b",function(){
//action 2
})
</>
虽然通过异步加载,避免了引入了a脚本,但是是否异步加载的 a 对应生成的 script 节点会有两个? action 1与 action 2谁先运行?
在我原先的理解 中,既然是完全独立的沙箱,则现实中由于网络的不确定性 action1,action2的执行顺序不定,并且连续YUI() .use的异步脚本加载应该是完全独立并行的,这样的话是很理想的独立环境,但是由于两次use的互相独立必然会造成 a 的重复加载。
妥协与解决
yui3 这里不像我想的那样完全独立,而是确实考虑了重复加载情况,将use中的异步部分实际上通过全局队列做成了串行执行,且在每次异步加载前先要剔除当前已经加载过的模块。正是由于上述解决方案,也确定了 action1,action2的顺序一定,一定先action1,后action2,可能有点失望,如果上述例子换成
YUI().use("a",function(){
//action 1
})
......
html
......
YUI().use("b",function(){
//action 2
})
若同时创建两个脚本节点 a,b添加到文档,并行下载a,b必然能提高整体性能,但是为了保证一般情况下不要重复对同一模块多次从server读取(特别是开启combo情况下),在yui3的解决方案下,则变成了:先生成 a 节点添加到文档,待a脚本载入后,删除b和a相交的模块(这里为空),再生成 b 节点添加到文档,yui3强制实行了串行化。
实现:
1. 每次use都会调用Loade的insert方法:
//全局控制加载队列 _queue = YUI.Env._loaderQueue, insert: function(o, type) { ....... //异步加载行为加入到队列 _queue.add(function() { self._insert(copy, o, type); }); //如果队列中没有正在执行的东西,就执行否则返回 this._continue(); }
在加载中将队列状态设置为正在执行,加载后设置为执行完毕,并标志加载过的模块到全局对象 。
_continue:function(){ //....//标志队列在执行 _queue.running = true; //.... } _finish:function(){ //.... //标志队列空闲 _queue.running = false; //.... }
2.而在将要真正创建 script节点进行异步加载前进行已加载模块剔除:
_reduce: function() {
// remove if already loaded
//如果前面 use 过了就从待加载模块列表中删除
}