3

我正在寻找一种方法来有条件地加载并保持一些 javascript 文件(外部和内部)的执行顺序,而无需任何库依赖。基本上,我要做的是仅在浏览器支持 localStorage 时才加载它们。

这基本上是我的外壳:

if (window.localStorage) {
//load up JS only if it's needed
    var body = document.getElementsByTagName('body')[0],
        js1 = document.createElement('script'),
        js2 = document.createElement('script'),
        js3 = document.createElement('script'),
        js4 = document.createElement('script'),
        js5 = document.createElement('script');

    js1.src = 'http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js';
    js2.src = 'http://www.google.com/jsapi';
    js3.src = 'my_file1.js';
    js4.src = 'my_file2.js';
    js5.src = 'my_file3.js';

    body.appendChild(js1);
    body.appendChild(js2);
    body.appendChild(js3);
    body.appendChild(js4);
    body.appendChild(js5);
} else { 
    //no localStorage support, display messaging
}

我尝试通过 createElement/body.appendChild 动态添加脚本节点,但这些似乎不起作用。

有没有简单的方法来实现这一目标?现在一切正常,但是 IE6 和 IE7 的人下载了他们甚至没有执行的脚本,这是我想要修复的。

4

3 回答 3

6

添加script节点应该可以正常工作。因为这些脚本将与添加它们的代码异步执行,所以您需要给它们一个回调来调用以按顺序执行下一件事。例如:

if (window.localStorage) {
    // Load the local storage stuff; once loaded, it'll call
    // `doTheNextThing`
    var script = document.createElement('script');
    script.type = "text/javascript";
    script.src = /* ... the URL of the script ... */;
    document.body.appendChild(script); // Or append it to `head`, doesn't matter
                                       // and `document.body` is convenient
}
else {
    // Skip loading it
    setTimeout(doTheNextThing, 10);
}

function doTheNextThing() {
    // ...
}

...您正在加载的动态脚本在加载后localStorage调用doTheNextThing的地方 - 因此,在有 的情况下localStorage,动态加载的脚本会调用doTheNextThing,但在没有的情况下,上面的代码会调用。请注意,我是故意从上面的代码中进行调用的异步(通过setTimeout):使其始终异步,无论它是如何被调用的,这样可以减少丢失错误的几率(例如,添加一些依赖于它被同步调用然后忘记测试的东西IE 上的小改动)。

更新:以上假设您可以控制正在加载的脚本,但您已经澄清您不是。在这种情况下,您需要做的是一次加载一个脚本并轮询它们提供的功能(通常是对象上的window属性,例如window.jQuery),如下所示(未经测试):

// Load the script designated by `src`, poll for the appearance
// of the symbol `name` on the `window` object. When it shows
// up, call `callback`. Timeout if the timeout is reached.
function loadAndWait(src, name, timeout, callback) {
    var stop, script;

    // Do nothing if the symbol is already defined
    if (window[name]) {
        setTimeout(function() {
            callback("preexisting");
        }, 10);
    }
    else {
        // Load the script
        script = document.createElement('script');
        script.type = "text/javascript";
        script.src = src;
        document.body.appendChild(script);

        // Remember when we should stop
        stop = new Date().getTime() + timeout;

        // Start polling, long-ish initial interval
        setTimeout(poll, 150);
    }

    function poll() {
         if (window[name]) {
             // Got it
             callback("loaded");
         }
         else if (new Date().getTime() > stop) {
             // Time out
             callback("timeout");
         }
         else {
             // Keep waiting, shorter interval if desired
             setTimeout(poll, 75);
         }
    }
}

...您将像这样用于 jQuery 加载:

loadAndWait(
    "http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js",
    "jQuery",
    10000, // ten seconds or whatever
    function(result) {
        // ...do the next one if result !== "timeout"
    }
 );

您可以loadAndWait在每个先前调用的回调中嵌套调用,也可以使用数组和计数器:

loadThese(
    [
        {   src:    "http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js",
            symbol: "jQuery"
        },
        {
            src:     "http://the-next-one",
            symbol:  "nextSymbol"
        }
    ],
    doTheNextThing
);

function loadThese(scripts, callback) {
    var index = 0;
    run("okay");

    function run(result) {
        var entry;

        if (result === "timeout") {
            callback(result);
        }
        else if (index < scripts.length) {
            entry = scripts[index++];
            loadAndWait(entry.src, entry.symbol, 10000, run);
        }
        else {
            callback("loaded");
        }
    }
}

在那里,loadThese设置一个循环来run依次加载每个脚本。

以上所有内容都是现成的,可能会被收紧和防弹,但你明白了。

题外话,但我的问题是:真的有这么多的代码,对于不能使用它来加载它的浏览器来说是个问题吗?除非文件变得更大,否则对于使用高级浏览器的用户来说,您实际上会减慢您的网站速度,而不会从其他人那里获得任何东西。在一定大小以下,连接到服务器以检索脚本的开销与传输脚本一样大。额外的东西是 50k 的代码吗?我会做一些基准测试来测试它是否真的有必要......也许是(也许你已经拥有了!),但值得一提......

离题更新localStorage:在您更新的问题中,您列出了五个单独的脚本,如果支持,您将下载这些脚本。即使假设您从各种 CDN 获得所有五个,这也是许多单独的脚本请求(无论是按常规方式完成还是按上述方式完成),每个必须一次处理一个。这是一个等待发生的页面加载性能问题。尽管(可能)失去了 CDN 和现有缓存的好处,但您可能会考虑抓取所有这些脚本,将它们组合起来,并将组合版本托管在一个文件中。请参阅YUI 性能“规则”中的“最小化 HTTP 请求” (我更喜欢术语“指南”,但无论如何)。它还将简化您的动态加载。

于 2010-10-19T23:01:46.867 回答
2

您可以使用 onload 和closure 函数的组合。类似于以下内容:

function loadScripts(index) {
    return function () {
        var e = document.createElement('script');
        e.src = scripts[index];
        document.body.appendChild(e);

        if (index + 1 < scripts.length) {
            e.onload = loadScripts(index + 1)
        }
    };
}

并像这样调用它:

loadScripts(0)();
于 2011-04-30T05:35:53.103 回答
0

我是来使用这个代码的。两个主要函数(addEvent 和 load_javascript)都可以在网上找到。
不过,我并没有试图减少下载大小:这是我可以加载资源的唯一方法。所以,也许Šime Vidas提出的想法对您来说是有意义的。

addEvent = function(elm, evType, fn, useCapture) {
    //Credit: Function written by Scott Andrews
    //(slightly modified)
    var ret = 0;

    if (elm.addEventListener) {
        ret = elm.addEventListener(evType, fn, useCapture);
    } else if (elm.attachEvent) {
        ret = elm.attachEvent('on' + evType, fn);
    } else {
        elm['on' + evType] = fn;
    }

    return ret;
};


    var left_to_load = 0;
    function init() {
        --left_to_load;
        if (left_to_load > 0) {
            return;
        }

        // all scripts are loaded now
        // proceed with your logic
    }

    // load js file and call function when done
    function load_javascript(src, callback) {
        var a = document.createElement('script');
        a.type = 'text/javascript';
        a.src = src;
        var s = document.getElementsByTagName('script')[0];
        s.parentNode.insertBefore(a, s);

        ++left_to_load;
        addEvent(a, 'load', callback, false);
    }



load_javascript('url1', init);
load_javascript('url2', init);
...
于 2010-10-19T23:03:42.820 回答