有条件地加载javascript(外部和内部)并保持执行顺序

mag*_*nta 3 javascript lazy-loading loading local-storage

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

这基本上是我的shell:

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
}
Run Code Online (Sandbox Code Playgroud)

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

有没有一种简单的方法来实现这一目标?现在一切正常,但IE6和IE7人们下载的脚本甚至没有执行,这就是我想要解决的问题.

T.J*_*der 5

添加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() {
    // ...
}
Run Code Online (Sandbox Code Playgroud)

...加载后localStorage调用stuff 的动态脚本所在的位置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);
         }
    }
}
Run Code Online (Sandbox Code Playgroud)

...你将这样用于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"
    }
 );
Run Code Online (Sandbox Code Playgroud)

您可以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");
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

在那里,loadThese设置一个循环使用run依次加载每个脚本.

以上所有内容都完全不在袖口,可能会收紧并防弹,但你明白了.

偏离主题,但我的问题是:是否真的有这么多代码,对于无法使用它加载它的浏览器来说,这是一个问题?除非得到一个文件很多更大,您实际上会减慢你的网站有先进的浏览器的用户没有获得太多的其他东西.低于一定的大小,连接到服务器以检索脚本的开销与传输脚本一样重要.额外的东西是50k的代码吗?我会做一些基准测试来测试它是否真的有必要......也许它(也许你已经有了!),但值得一提......

离题更新:在更新的问题中,如果localStorage支持,则列出要下载的五个单独脚本.即使假设你得到所有五个来自不同的CDN,这是一个很大单个脚本请求(通常的方式是否完成或以上),其中的每一个具有被处理一次一个.这是一个等待发生的页面加载性能问题.尽管(可能)失去了CDN和现有缓存的好处,但您可能会考虑抓住所有这些脚本,将它们组合在一起,并将组合版本托管在一个文件中.请参阅YUI性能"规则 "中的"最小化HTTP请求" (我更喜欢术语"指南",但无论如何).它还可以简化您的动态加载.