我的模組載入系統 v6

來源:互聯網
上載者:User

模組系統是我架構的核心模組的一部分,與domReady徹底地整合在一起。當然核心模組還提供了其他常用方法,如數組化,類型判定,簡單的事件綁定...本版本主要是對類型判定進行最佳化與重寫模組載入,以更優雅的方式處置循環相依性的問題。

(function(global,DOC){    var     _dom = global.dom,    hash = "#",    namespace = escape(DOC.URL.split(hash)[0]),    HEAD = DOC.head || DOC.getElementsByTagName("head")[0],    class2type = {        "[object HTMLDocument]"   : "Document",        "[object HTMLCollection]" : "NodeList",        "[object StaticNodeList]" : "NodeList",        "[object IXMLDOMNodeList]": "NodeList",        "[object DOMWindow]"      : "Window"  ,        "null"                    : "Null"    ,        "NaN"                     : "NaN"     ,        "undefined"               : "Undefined"    },    toString = class2type.toString;    /**     * 糅雜,為一個對象添加更多成員     * @param {Object} target 目標對象     * @param {Object} source 屬性包     * @param {Boolean|undefined} override     * @return  {Object} 目標對象     */    function mix(target, source, override) {        var key,  ride = (override === void 0) || override;        for (key in source) {            if (ride || !(key in target)) {                target[key] = source[key];            }        }        return target;    }    /**     * @class dom     * dom Framework擁有兩個命名空間,     * 第一個是escape(DOC.URL.split("#")[0]),根據頁面的地址動態產生     * 第二個是dom,我們可以使用別名機制重寫它     * @namespace dom     */    var dom = function(expr,context){        if(typeof expr === "function"){            dom.ready(expr, true);        }else{            if(!dom.fn)                throw "must load the 'node' module!"            return new dom.fn.init(expr,context);        }          }    mix(dom,{        uuid:1,        HTML:DOC.documentElement,        HEAD:HEAD,        rword:/[^, ]+/g,        rreadystate: /loaded|complete|undefined/i,        "@name":"dom",        "@debug":true,        "@emitter": DOC.dispatchEvent ? "addEventListener" : "attachEvent",        /**         * 別名機制(相當於jquery的noConflict)         * @param {String} name 新的命名空間         */        exports: function(name) {            _dom && (global.dom = _dom);            name = name || dom["@name"];            dom["@name"] = name ;            global[name] = global[namespace] = this;        },        /**         * 數組化         * @param {ArrayLike} nodes 要處理的類數組對象         * @param {Number} start 可選。要抽取的片斷的起始下標。如果是負數,從後面取起         * @param {Number} end  可選。規定從何處結束選取         * @return {Array}         */        slice: function (nodes, start, end) {            for(var i = 0,n = nodes.length, result = []; i < n; i++){                result[i] = nodes[i];            }            if (arguments.length > 1) {                return result.slice(start , (end || result.length));            } else {                return result;            }        },        /**         * curry化         * @param {Any} a         * @return {Function} 新函數         */        K:function (a) {            return function() {                return a;            };        },        /**         * 用於取得資料的類型或判定資料的類型         * @param {Any} obj 要檢測的東西         * @param {String} str 要比較的類型         * @return {String|Boolean}         */        type : function(obj, str){            var result = class2type[ (obj == null || obj !== obj )? String(obj) :  toString.call(obj)  ] || obj.nodeName || hash;            if( result.charAt(0) === hash){//相容舊式瀏覽器與處理個別情況,如window.opera                if(obj.window==obj){                    result = 'Window'; //返回構造器名字                }else if(obj.nodeType === 9) {                    result = 'Document';//返回構造器名字                }else if(  obj.callee ){                    result = 'Arguments';//返回構造器名字                }else if(isFinite(obj.length) && obj.item ){                    result = 'NodeList';//處理節點集合                }else if(obj.open && obj.send ){                    result = "XMLHttpRequest";                }else{                    result = toString.call(obj).slice(8,-1);                }            }            if(str){                return str === result;            }            return result;        },          /**         * 用於調試         * @param {String} s         */        log:function(s){            global.console && global.console.log(s);        },        /**         * 產生索引值統一的對象,用於高速化判定         * @param {Array} array         * @param {Number} val 可選,預設為1         * @return {Object}         */        oneObject : function(array, val){            if(typeof array == "string"){                array = array.match(dom.rword) || []            }            var result = {},value = val !== void 0 ? val :1;            for(var i=0,n=array.length;i < n;i++){                result[array[i]] = value;            }            return result;        }    });    "Boolean,Number,String,Function,Array,Date,RegExp,Window,Document,Arguments,NodeList,XMLHttpRequest".replace(dom.rword,function(name){        class2type[ "[object " + name + "]" ] = name;    });    // http://wp.moooori.com/archives/1911    var     rmodule =  /([^(\s]+)\(?([^)]*)\)?/,    //  rreadystate = /loaded|complete|undefined/i,    method = DOC.dispatchEvent ? "onload" : "onreadystatechange",    stack = [],//用於放置負回調    checks = [],//用於放置正向回調或模組本身的wrapper函數    status = {};//用於記錄每個模組載入狀態0 未註冊 1 登入 2 已載入 3已執行    /**     * 用於類比opera的script onerror     * http://d.hatena.ne.jp/NeoCat/20110206/1296934235     * @param {String} name 模組名     * @param {String} url  模組的路徑     * @param {Element} node 為載入這個模組臨時產生的script節點     */    function fixOperaError(name, url, node){        var iframe = DOC.createElement("iframe");        var code = '<script src="'+dom.baseURL+'"><\/script>'+            '<script src="'+url+'" onload="this.ownerDocument.x = 1"><\/script>';        iframe.style.display = "none";        HEAD.appendChild(iframe);        var d = iframe.contentDocument;        iframe.onload = function(){            if(d.x == void 0){                removeScript(name, node, true);            }            iframe.onload = null;//opera無法在iframe被事件綁定時被移除            HEAD.removeChild(this);        };        try{            d.write(code);            d.close();        }catch(e){};    }    //執行並移除所有依賴都具備的模組或回調    function resolveCallbacks(){        for(var i = 0, obj; i < checks.length; i++){            obj = checks[i];            for(var name in status){                if(status[name] == 3 && obj.deps["@"+name]){                    delete obj.deps["@"+name];                    obj.length--;                    dom["@debug"] &&  dom.log(obj.length+" "+name);                }            }            if(!obj.length){                obj.callback()                checks.splice(i--,1);            }        }    }    /**     * 為載入模組而臨時產生一個script節點     * @param {String} name 模組名     * @param {String} url  模組的路徑     */    function appendScript(name, url){        var node = DOC.createElement("script");        url = url  || dom.basePath +"/"+ name + ".js" + (dom.DEBUG ? "?t="+new Date : "");        node.charset = "utf-8";        node.async = true;        node.onerror = function(){            removeScript(name, this, true);        }        node[method] = function(){            if (dom.rreadystate.test(this.readyState) ){                for(var i =0 ; i < checks.length ;i++){                    resolveCallbacks()                }                removeScript(name, this );            }        }        node.src =  url+"?timestamp="+(+new Date);        global.opera && fixOperaError(name, url, node);        HEAD.insertBefore(node,HEAD.firstChild);    }    /**     * 移除臨時產生的script節點     * @param {String} name 模組名     * @param {Element} node 為載入這個模組臨時產生的script節點     * @param {Boolean} error 是否載入失敗     */    function removeScript(name, node, error){        var parent = node.parentNode;        if(parent && parent.nodeType === 1){            if(error || status[name] === 1){                dom.stack('dom.log("fail to load module [ '+name+' ]")');                dom.stack(true);//列印錯誤堆棧            }            if (node.clearAttributes) {                node.clearAttributes();            } else {                node[method] = node.onerror = null;            }            parent.removeChild(node);        }    }    mix(dom, {        mix:mix,        //綁定事件(簡化版)        bind : DOC.dispatchEvent ? function(el, type, fn, phase){            el.addEventListener(type,fn, phase);        } : function(el, type, fn){            el.attachEvent("on"+type, function(){                fn.call(el,event);            });        },        stack:function(fn){            if(fn === true){                while(fn = stack.pop()){                    fn();                }            }else if(typeof fn === "string"){                stack.push(new Function(fn));            }else if(typeof fn === "function"){                stack.push(fn);            }        },        /**         * <a href="http://www.cnblogs.com/rubylouvre/archive/2011/02/10/1950940.html">核心模組所在路徑</a>         * @property         * @type String         */        basePath:(function(url, scripts, node){            scripts = DOC.getElementsByTagName("script");            node = scripts[scripts.length - 1]            url = node.hasAttribute ?  node.src : node.getAttribute('src', 4);            dom.baseURL = url;            return url.substr( 0, url.lastIndexOf('/'));        })(),        /**         * 請求模組         * @param {String|Array} deps 依賴列表,如果是字串請用逗號隔開,如果要指定具體路徑請寫到小括弧中         * @param {Function} module 正向回調         * @param {Function} errback 負向回調         * @param {Boolean} ready 是否延遲到DOM樹建完才執行回呼函數         */        require:function(deps,callback,errback){            var names = [];            (deps +"").replace(dom.rword,function(url,name,match){                match = url.match(rmodule);                name  = match[1];                if(!status[name]){ //防止重複產生節點與請求                    status[name] = 1;                    if(name !== "ready"){                        appendScript(name,match[2]);//載入JS檔案                        dom.stack(errback);//壓入錯誤堆棧                    }                }                if(!names["@"+name]){//防止"node,oop,node"的情況                    names.push(name);                    names["@"+name] = 1;                }            });            this.provide(names,callback);        },        /**         * 定義模組         * @param {String} name 模組名         * @param {String} dependList 依賴列表         * @param {Function} module 模組本身         */        define:function(name,deps,callback){            if(typeof deps == "function"){//處理只有兩個參數的情況                callback = deps;                deps = "";            }            status[name] = 2;            function wrap(){                status[name] = 3;                //這裡必須把已添加了新能力的dom命名空間對象傳入去,放便其他模組第一時間能使用這些新能力                callback();            }            wrap._name = name;            //如果是Null 字元串則置換為空白數組            this.require(deps,wrap);          },        /**         * 裝配模組         * @param {Array} array 模組列表         * @param {Function} callback 回呼函數         */        provide:function(names,callback){             checks.unshift({                name:callback._name || "",                callback:callback,                deps:names,                length:names.length            });          }    });    /**     * domReady機制     * @param {Function} fn 回呼函數     * @param {Boolean} more 是否載入指定的模組     */    var Ready = dom.ready = function(fn){        if ( Ready.list) {            Ready.list.push( fn );        }else{            fn();        }    };    Ready.list = [];    function fireReady(){        if(Ready.list){            for(var i=0, fn;fn = Ready.list[i++];)                fn();            status.ready = 3;            delete Ready.list;        }    };    function doScrollCheck() {        try {            dom.HTML.doScroll("left");            fireReady();        } catch(e) {            setTimeout( doScrollCheck, 0);        }    };    //開始判定頁面的載入情況    if ( DOC.readyState === "complete" ) {        fireReady();    }else {        dom.bind(DOC, (DOC.dispatchEvent ? "DOMContentLoaded" : "readystatechange"), function(){            if (DOC.dispatchEvent || DOC.readyState === "complete") {                fireReady();            }        });        try {            //http://bugs.jquery.com/ticket/4787 http://cmc3.cn/            //在IE6下,內嵌頁面如果重設了document.domain,訪問window.frameElement拋錯,            //但是經過多次try catch後,才能訪問該值            var toplevel = global.frameElement == null;        } catch(e) {};        if ( dom.HTML.doScroll && toplevel ) {            doScrollCheck();        }    }    dom.exports();})(this,this.document);//2011.7.11//@開頭的為私人的系統變數,防止人們直接調用//dom.check改為dom["@emitter"]//dom.namespace改為dom["@name"]//去掉無用的dom.modules//最佳化exports方法

每個模組的定義如下(請確保模組名與檔案名稱要一致):

(function(global,DOC){    var dom = global[escape(DOC.URL.split("#")[0])];    dom.define("class", "lang",function(){       dom.log("class模組")    });})(this,this.document);

使用:

//這裡的回呼函數會class模組載入後,並且待到domReady之後執行。//http://www.cnblogs.com/rubylouvre/archive/2011/07/12/2104777.html by 司徒正美      dom.require("class,ready",function(){             alert(1000)      });

相關連結我的模組載入系統 v5

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.