在上一篇javascript動態加載中,提到了使用同步加載策略這一個方式來實現如
復制代碼 代碼如下:
Using("jquery");
Using("User");
$("#ID").click(function(){
var user = new User();
user.name = "xx";
user.show();
});
由於JS是單線程的,所以同步策略帶來的壞處不少,比如阻止之後的代碼運行、造成浏覽器假死等問題。
使用異步策略又難以實現先導包 後使用的效果。只能采用callback的形式來進行,這又不是UsingJS想要實現的,畢竟jQuery的getScript函數就可以實現這一方式。
經過一番思考,到底怎麼解決導包而且是異步的方式,最後得出一個解決方案。先來看一下采用這個方案後的編程方式。
復制代碼 代碼如下:
<div id="panel"></div>
<script type="text/javascript" src="js/using-0.4.js"></script>
<script type="text/javascript">
Using("jq");
Using("jq");
Using("Http");
Using.asyn(function(){
$("#panel").click(function(){
alert("Using jquery object");
});
Using.fetch("Http",function(){
var http = new Using.Modules.Http();
http.set("xxx");
http.show();
});
});
</script>
如上代碼所示,總體來說與同步策略沒有太大的修改,只是導入了兩次jquery,這裡顯然是需要處理成只導包一次,並且裡面增加了一個Using.asyn函數,具體這個函數做什麼用,之後會分析。
都知道,異步策略,是不影響當前運行的,那麼,我導入的包,假如正在加載,而其後的代碼也正在運行,兩者之間也剛好存在依賴關系,那麼就會出現異常,怎麼解決這兩者之間的關系,目前唯一的解決辦法就是回調函數。
按照Using的思想,必須是先導包後使用。異步的解決辦法就是在模塊使用之前,並不真的去進行文件拉取,而是將需要的JS文件放置到一個對象當中,比如Array,當有真正需求的時候,再逐個進行加載。來看看
復制代碼 代碼如下:
Using("jq");
Using("jq");
Using("Http");
是怎麼工作的。上一段代碼
復制代碼 代碼如下:
var moduleList = [];
Using.fn = Using.prototype;
Using.fn.initialize= function(module){
!this.exist(moduleList,module) ? moduleList.push(module) : null;
}
這段代碼是略去上下文,截取的Using的原型中的一個初始化方法,從代碼得知,其主要的職責就是將需要加載的模塊放置到moduleList中,並且進行判斷,假如moduleList中含有當前需要加載的模塊,那麼,不進行任何操作。
那麼,什麼時候進行加載呢?這個就用到了之前提到的Using.asyn方法,也就是通知Using,現在需要異步加載文件了,並且,在加載完畢之後調用Using.asyn函數的回調函數。同樣上一段代碼
復制代碼 代碼如下:
Using.asyn = function(callback){
Using.fn.script(callback);
}
從代碼只能簡要的看出,Using.asyn函數調用了Using.fn.script函數,並且將回調函數傳給了它。自然,就需要看看其又是怎麼工作的。
復制代碼 代碼如下:
Using.fn.script = function(callback){
var _this = this,
complete = 0,
count = moduleList.length,
len = 0;
if(count < 1){
return;
}
var loadScript = function(){
while(len < count){
_this.ajax(Using.Config[moduleList[len]],function(){
complete++;
if(complete >= count){
callback();
}
});
len++;
}
}
!Using.Config ? _this.ajax("/js/config",function(){
loadScript();
}) : loadScript();
}
首先看Using.Config,就是上一篇提到的模塊配置文件,以通知Using通過模塊名來加載相應的模塊文件。
其次就是通過內部函數loadScript來做模塊文件的加載,通過一個計數器complete來判斷當前已經加載了幾個模塊,當所有模塊加載完畢,則調用回調函數。
整合以上代碼,整個思路就是說,通過Using對象來導包,並記錄,通過Using.asyn來通知Using進行異步加載,最後由Using.fn.script來實現異步加載並執行回調函數。
還注意到Using.fetch函數,整個函數主要是為了解決當代碼運行到一定程度或者某一個需求才要加載的文件,類似於$.getScript文件,在加載之前會進行判斷,判斷當前需要的模塊是否已經加載過,如果加載過則直接執行回調函數。
這一次UsingJS的改動,主要是為了將同步策略改為異步策略,但是同樣遺留有很多問題,比如要進行類似$(document).ready,只在文檔加載完畢的時候才執行,本身來說,實現這個一個效果並不難,而是編寫代碼時,腦子凌亂了,一時沒辦法解決Using.asyn多次調用時,由於異步而產生的多次加載同一個模塊,又或者各種莫名其妙的問題,一時沒有了頭緒,故,將此問題後延,一步一步的解決之。
還有便是導包的順序,不能任意順序,當時也想做成任意導包,通過添加依賴關系,來做到由代碼解決加載順序,但是又想到,這個做法沒有什麼很大的實際意義,編碼人員肯定知道文件之間的依賴關系的,如果編碼的人不知道文件的加載順序,就是使用<script>標簽形式,照樣會出錯,而做成依賴關系不僅增加了Using的體積,更重要的是做了一件重復的事情。不知道這樣理解對不對。