本版本的重要更新是完全實現AMD規范。整個框架根據此新加載器重寫,因此可以方便調用老外用AMD規范寫好的JS庫了.
- 日志打印可以通過設置$.core.level排除某些不重要的日志打印。
- 添加config方法來設置框架的一些重要信息或對模塊進行別名。
- 在VS系列實現智能提示。
有關本模塊加載系統的使用,可以到這里詳看教程.新加載器是同時支持AMD與玉伯搞的seajs的CMD.如果你在模塊定義中的回調帶有"require","exports","modules"中的任兩個,加載器就視為CMD.不過無視是AMD與CMD,函數里面require, modules, exports都是可用的,完全可以像node.js這樣加載模塊.
有關require方法的模塊標識,可以見這里
有關為什么要用AMD來管理我們的腳本,我想做過一些大項目的人應該心中有數,這不是一個合並JS所能取替的,詳見此文。
下面揭載我的加載器的運作機理,姑且從一個沒有依賴的模塊着手,比如
$.require("$lang_fix", function(){ console.log("xxxxxxxxxxxxxx") })
不用說,進入$.require(希望大家看這東西時,打開源碼對照着看。)
String(list).replace( $.rword, function(el){ }) el = "$lang_fix"
進入Module._resolveFilename
url = $.core.base + el + ".js"
初次加載,肯定沒有在modules中注冊, modules[url]為undefined
於是進入 loadJS( url, id );
- 創建一個iframe,
- 在第一個script節點 用nick, Ns, nick分別保存url, $, innerDefine
- 在第二個script節點 用url去加載目標節點
第二個節點視情況不同分別綁定onreadystatechange, onload, onerror
值得注意的是我們在注冊模塊時state還是為undefined,它會在innerDefine中修改state。
然后我們通過iframe中的script加載模塊,而模塊一般是這樣的格式define("lang_fix", function( ){ //==========略============ })
這里的define實質上是innerDefine
innerDefine里面做了幾個很重要的事情(我們現在只需看第一,第二)
- 第一個事情把lang_fix這第一個參數換掉,換成nick, nick就是加載它的那個script的src。
- 第二個事情是將它對應的模塊的狀態改為1,也是modules[url].state = 1。
- 第三個事情是$, exports, require, module等對象強塞進模塊工廠
- 第四個事情是轉交真正的$.define去處理
在第二事情中,我們將模塊的狀態修改了,於是節點執行onreadystatechange/onload時
if(/loaded|complete|undefined/i.test(this.readyState) }{ Ns._checkDeps(); Ns._checkFail(self.document, nick); }
Ns._checkFail發揮效力
_checkFail : function( doc, id, error ){ doc && (doc.ok = 1); if( error || !modules[ id ].state ){ this.log("Failed to load [[ "+id+" ]]"+modules[ id ].state); } },
如果是死鏈,那無法調用define函數,也就無法調用innerDefine,狀態為undefine
!modules[ id ].state == true,於是打印錯誤日志,當然以后我們討論一下,是不是該throw
如果在舊式opera,它是會無法進入onreadystatechange, onload, onerror任一回調
在_checkFail中,我們會修復doc.ok = 1,那么在iframe中onload中我們檢測doc.ok不等於1時,
就在_checkFail傳入第三個參數true,讓它打印錯誤日志
如果在FF,chrome, IE9,它們就會進入onerror回調,那里的調用代碼中
Ns._checkFail(self.document, nick, true)
因此也順利檢測到死鏈。
反正只要加載失敗,我們就立即把對應iframe移出DOM樹!
好了,如果成功加載,我們就通過innerDefine到達$.define,並也把對應iframe移出DOM樹
$.define干了如下幾個事情
- 第一個事情,檢測第二個參數是否為布爾,是說明其是補丁模塊,如果布爾值為true,說明這個補丁模塊對這個瀏覽器是沒有用的,直接return,不執行它的模塊工廠了。如果為false,比如IE6,補丁模塊對它總是有用,先去掉此參數,繼續往下執行。
- 第二個事情,檢測參數個數,如果只有兩個,說明只有模塊名與模塊的回調(亦有可能不是回調),那么我們插入一個空數組作為依賴列表
- 第三個就是檢測第三個參數是否為函數,不是函數,比如說是個對象,我們要將它塞入一個函數
比如
define({ aaa:2 })
經上面幾次轉換,依次變為
define("http://xxxxxxx/aa.js",{ aaa:2 }); //----> define("http://xxxxxxx/aa.js" ,[],{ aaa:2 }); //----> define("http://xxxxxxx/aa.js" ,[], function(){ return { aaa:2 } }); //----> $.require([], function(){ return { aaa:2 } }), "http://xxxxxxx/aa.js")
最后我們將參數的順序重排一下,再次調用$.require
//0,1,2 --> 1,2,0 this.require( args[1], args[2], parent );
由於lang_fix是沒有依賴的,因此dn === cn 相當於 0 == 0,執行install( id, args, factory );
install簡而言之是將模塊工廠執行,將state改為2。
我們還需要注意一下,我們每次調用$.require或加載一個腳本時都執行_checkDeps方法。
當lang_fix的狀態改為2后
$.require("$lang_fix", function(){ console.log("xxxxxxxxxxxxxx") })
這個回調也將執行!
控制台打印xxxxxxxxxxxxxxxx
源碼位於這里