先做兩個鏈接:
之前兩篇都介紹了,通過動態加載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) 謝謝