先做两个链接:
之前两篇都介绍了,通过动态加载JS文件或者说JS模块,是怎么一步一步实现。
首先是通过同步策略来实现模块加载与回调函数之间进行分离,接着是通过异步策略来实现模块加载与回调函数之间进行分离。
这一篇,主要是为了说说怎么优化异步策略,并且实现了随意加载(非任意顺序加载模块),页面Ready之后加载文件。先接一下上一篇遗留下来的问题
1、页面Ready之后进行加载
2、随意添加模块 进行加载
看第一个问题,这个问题其实还是比较简单的,主要是监听页面的DOMContentLoaded事件,这里就不多讲解,网络上搜索,一堆答案,直接上代码。
Using.ready = function(callback){ readyList.push(callback); if(document.addEventListener){ document.addEventListener("DOMContentLoaded",_ready,false); return; } // for IE var domReady = function(){ try{ document.documentElement.doScroll("left"); _ready(); }catch(ex){ setTimeout(domReady,1); return; } } domReady(); }
这一段代码中最难以理解的应该就是
document.documentElement.doScroll("left");
这里其实是IE的页面加载完毕事件,简单说就是IE里面标签加载完毕之后,是可以操作Scroll的,那就根据此原理来判断IE中页面是否加载完毕。
里面有一个_ready函数,这个函数就是用来做页面加载完毕之后执行所有加载的函数。贴一下代码
(编辑一下这一段:页面加载完毕Ready函数并不是我们思想中所认为的原生JS的window.onload,简单说只是页面中DOM结构的加载完毕,具体信息,可自行百度google之)
var readyList = []; var _ready = function(){ while(readyList.length > 0){ var func = readyList.shift(); func(); } document.removeEventListener("DOMContentLoaded",_ready,false); }
下面就是本博文的重点了。还是先看一下代码
Using.asyn = function(callback){ asynQueue.push(callback); if(!_execAsyn.isRunning){ _execAsyn(); } }
还是通知Using要加载所需要的模块了,只不过里面加入了一个asynQueue数组和_execAsyn函数,他们的作用分别是
asynQueue是用来保存异步加载之后要回调的函数,没什么好解释的,是一个数组,可以理解为创建了一个函数的队列
_execAsyn是用来执行保存的那些回调函数的,即将所保存的函数逐一执行。看一下代码,代码中对每行的作用都进行了注释
var _execAsyn = function(){ // 创建一个变量来缓存需要执行的函数 var func = null; // 如果队列中还有未执行的函数 则进行执行操作 if(asynQueue.length > 0){ // 将_execAsyn函数修改为运行状态 _execAsyn.isRunning = true; // 得到队列中第一个需要执行的函数 func = asynQueue.shift(); // 调用异步加载模块Using.fn.script函数 并传入加载完毕之后需要执行的回调函数 Using.fn.script(function(){ // 当前需要执行的函数 func(); // 迭代_execAsyn 直到队列中没有需要执行的函数 _execAsyn(); }); // 若队列中没有需要执行的函数 }else{ // 则将_execAsyn运行状态改为false _execAsyn.isRunning = false; } }
这个函数,解释起来没什么特别的,说白了就是一个一个的执行需要执行的函数。那么,唯一需要注意的就是为什么操作队列的时候没有采用循环,而是使用迭代。那原因就是
1、队列中随时可能有新的函数需要执行,采用循环的话,可能执行不到最新的函数,因为函数总是插入到队列的尾部
2、 Using.fn.script是异步的,如果是循环的话,当前函数还没有执行完,可能下一个函数就已经进入了执行状态。那么,本身来说,同时执行几个函数,速率上可能会更高,为什么这里还要限制其多个函数并行呢?原因也很简单,因为每一次执行队列中的函数,可能都需要加载相应的模块,那么如果刚好有两个或者多个依赖相同模块的函数需要执行,而且并行执行,就可能出现同一个模块加载多次,并可能造成后续的函数执行不了,出现异常。
整个UsingJS的核心部分就这些。在其中我加入了Using.Class.create函数,这个函数在javascript动态加载文章的末尾有提到。
最后看一下页面使用情况:
<script type="text/javascript" src="js/using-0.4.2.min.js"></script> <script type="text/javascript"> Using("jq"); Using("UserView"); Using("jq"); Using.ready(function(){ Using.asyn(function(){ $("#panel").click(function(){ alert("by jquery"); }); }); }); Using.ready(function(){ Using("Http"); Using.asyn(function(){ var http = new Using.Modules.Http(); http.set("xxx"); http.show(); }); Using.asyn(function(){ var h = new Using.Modules.Http(); h.set("ooo"); h.show(); }); Using("jq"); Using.asyn(function(){ $("#panel").click(function(){ alert("loaded jquery"); }); }); }); </script>
这个一段代码,刻意进行重复加载,多次Ready事件和Ready之后进行Using导包。
有一个特别需要注意的地方
Using("Http"); Using.asyn(function(){ var http = new Using.Modules.Http(); http.set("xxx"); http.show(); }); // 假如在这个地方使用 // var ht = new Using.Modules.Http(); // 是会报Using.Modules.Http不是一个constructor // 原因就是 // 任何操作都是异步的,当执行此句时Using("Http")这个模块载入可能还没有完成 // 这一点是仲谋给多个朋友进行使用时会犯的错误 总以为导包之后 万事大吉 // 是的 本身应该是这样 导包之后 在任何地方都可以随意引用 // 但是总得有个前提吧 那就是模块得加载完毕 // 所以 还请将所有的代码都写在Using.asyn之内 Using.asyn(function(){ var h = new Using.Modules.Http(); h.set("ooo"); h.show(); });
UsingJS下载(我只开了一个360的云盘 哈哈 简单嘛 呵呵 以本人的技术 还不至于去开一个github来 练习练习再说)
PS:希望下载的朋友 发现问题或者有更好的想法 提交于我 可以直接留言或者邮件我(sunjiawei1986@163.com) 谢谢