记点东西
标签类目:javascript loader
2009年11月16日前台

没有评论
8,150 views

QQ群前端使用的 javascript loader

开始主要想看一下qq群所使用的javascript模板,后来发现他们用的也是一个jquery的插件。
不过这个加载写的也很不错,分析了一下,原JS: http://qun.qq.com/god/m/js/loader.zh-cn.js

; (function() {
    var window = this,
    undefined,
    jLoader = window.jLoader = window.jL = {
        _version: "1.2.1",
        _sequence: [],
        _queue: {},
        _xhr: function() {
            return window.ActiveXObject ? new ActiveXObject("Microsoft.XMLHTTP") : new XMLHttpRequest();
        },
        // 直接加载脚本
        _script: function(uri, context) {
            context = context || document;
            var head = context.getElementsByTagName("head")[0] || context.documentElement,
            script = context.createElement("script");
            script.type = "text/javascript";
            script.src = uri;
            head.appendChild(script);
            return script;
        },
        _style: function(uri, context) {
            context = context || document;
            var head = context.getElementsByTagName("head")[0] || context.documentElement,
            link = context.createElement("link"),
            links = context.getElementsByTagName("link", context);
            link.type = "text/css";
            link.rel = "stylesheet";
            link.href = uri;
            if (0 < links.length) {
                 var _last = links[links.length - 1];
                 _last.parentNode.insertBefore(_link, _last.nextSibling);
             } else {
                 head.appendChild(link);
             }
             return link;
         },
         _replace: function(pattern, uri, context) {
             if (!pattern || !uri) {
                 return;
             }
             context = context || document;
             var links = context.getElementsByTagName("link", context);
             for (var i = links.length - 1; i >= 0; i--) {
                if ( - 1 != links[i].href.indexOf(pattern)) {
                    links[i].parentNode.removeChild(links[i]);
                }
            }
            if (uri) {
                jLoader._style(uri, context);
            }
        },
        // 将脚本内容插入当前页面来执行
        _globalEval: function(data, context) {
            if (data && /\S/.test(data)) {
                context = context || document;
                var head = context.getElementsByTagName("head")[0] || context.documentElement,
                script = context.createElement("script");
                script.type = "text/javascript";
                if (jLoader._excute) {
                    script.appendChild(context.createTextNode(data));
                } else {
                    script.text = data;
                }
                head.insertBefore(script, head.firstChild);
                head.removeChild(script);
                return true;
            }
            return false;
        },
        // 执行回调方法数组。有可能请求多次,绑定了多个的回调方法。
        _call: function(mark) {
            if (jLoader._queue[mark].callbacks) {
                var cl = jLoader._queue[mark].callbacks.length;
                for (var j = 0; j < cl; j++) {
                    jLoader._queue[mark].callbacks[j][0](jLoader._queue[mark].callbacks[j][1]);
                }
            }
        },
        // 这是一个顺序加载的过程,每加载一个JS,都会放入sequence队列,如果depend=true,则该脚本之前的脚本全部加载完后该脚本才执行
        _inject: function() {
            var len = jLoader._sequence.length;
            for (var i = 0; i < len; i++) {
                var seq = jLoader._queue[jLoader._sequence[i]];
                if (!seq.done) {
                    if (!seq.response) {
                        return;
                    } else {
                        seq.done = true;
                        seq.response = [seq.response, ";jLoader._call(\"", jLoader._sequence[i], "\");"].join("");
                        jLoader._globalEval(seq.response);
                    }
                }
            }
        },
        scripted: function(mark) {
            if ("undefined" == typeof jLoader._queue[mark]) {
                return false;
            } else {
                if (jLoader._queue[mark].done) {
                    return true;
                } else {
                    return false;
                }
            }
        },
        script: function(options) {
            options = options || {};
            // 参数不全
            if (!options.mark || !options.uri)
            return;
            options.depend = options.depend || false;
            options.params = options.params || {};
            options.onload = options.onload || null;
            // 如果没有依赖关系和回调方法,则直接引入
            if (!options.onload && !options.depend) {
                jLoader._script(options.uri);
                jLoader._queue[options.mark] = {
                    uri: options.uri,
                    response: null,
                    done: true
                };
                return;
            }
            // 是否为第一次请求
            if ("undefined" == typeof jLoader._queue[options.mark]) {
                jLoader._queue[options.mark] = {
                    uri: options.uri,
                    response: null,
                    done: false
                };
                // 设置回调方法和回调的参数
                if ("function" == typeof options.onload) {
                    jLoader._queue[options.mark].callbacks = [[options.onload, options.params]];
                }
            } else {
                // 虽然不是第一次请求,但有没有真正加载。没有真正加载的原因此时正在异步请求脚本内容
                if (false == jLoader._queue[options.mark].done) {
                    // 如果已经设置为callbacks, 则追加callbacks, 前面设置的callbacks是数组
                    if ("function" == typeof options.onload) {
                        if ("undefined" == typeof jLoader._queue[options.mark].callbacks) {
                            jLoader._queue[options.mark].callbacks = [[options.onload, options.params]];
                        } else {
                            var cl = jLoader._queue[options.mark].callbacks.length;
                            jLoader._queue[options.mark].callbacks[cl] = [options.onload, options.params];
                        }
                    }
                } else {
                    // 已真正加载,直接执行回调方法
                    if ("function" == typeof options.onload) {
                        options.onload(options.params);
                    }
                }
                return;
            }
            // 将脚本按顺序放入队列,self._inject会用到
            var len = jLoader._sequence.length;
            if (options.depend) {
                jLoader._sequence[len] = options.mark;
            }
            var xhr = jLoader._xhr();
            xhr.onreadystatechange = function() {
                if (4 == xhr.readyState) {
                    // 如果设置了依赖关系,则检查依赖。如果没有设置,则执行脚本内容和回调函数
                    if (options.depend) {
                        jLoader._queue[options.mark].response = xhr.responseText;
                        jLoader._inject();
                    } else {
                        jLoader._queue[options.mark].done = true;
                        var response = [xhr.responseText, ";jLoader._call(\"", options.mark, "\");"].join("");
                        jLoader._globalEval(response);
                    }
                }
            };
            xhr.open("GET", options.uri, true);
            xhr.send("");
        }
    };
    // 很好,很强大,因为getTime()结果不一样,所以用这种方法可以准确的定位到某一个类有没有执行
    jLoader._excute = false;
    var root = document.documentElement,
    script = document.createElement("script"),
    id = "script" + (new Date).getTime();
    script.type = "text/javascript";
    try {
        script.appendChild(document.createTextNode("window." + id + "=1;"));
    } catch(e) {}
    root.insertBefore(script, root.firstChild);
    if (window[id]) {
        jLoader._excute = true;
        delete window[id];
    }
    root.removeChild(script);
})();
返回顶部