如何使用jQuery $ .getScript()方法包含多个js文件

时间:2012-08-03 20:58:14

标签: javascript jquery html promise

我正在尝试动态地将javascript文件包含到我的js文件中。我做了一些关于它的研究,发现jQuery $ .getScript()方法将是一种理想的方式。

// jQuery
$.getScript('/path/to/imported/script.js', function()
{
    // script is now loaded and executed.
    // put your dependent JS here.
    // what if the JS code is dependent on multiple JS files? 
});

但是我想知道这个方法是否可以一次加载多个脚本?为什么我问这是因为有时我的javascript文件依赖于多个js文件。

提前谢谢你。

18 个答案:

答案 0 :(得分:290)

答案是

您可以将promises与getScript()一起使用,并等待所有脚本加载,例如:

$.when(
    $.getScript( "/mypath/myscript1.js" ),
    $.getScript( "/mypath/myscript2.js" ),
    $.getScript( "/mypath/myscript3.js" ),
    $.Deferred(function( deferred ){
        $( deferred.resolve );
    })
).done(function(){

    //place your code here, the scripts are all loaded

});

FIDDLE

ANOTHER FIDDLE

在上面的代码中,在$()中添加Deferred并将其解析就像在jQuery调用中放置任何其他函数,如$(func),它与

相同
$(function() { func(); });

即。它等待DOM准备就绪,所以在上面的示例中$.when等待所有脚本加载,因为$.Deferred调用了DOM准备就绪它在DOM就绪回调中解析。


为了更通用的使用,一个方便的功能

可以像这样创建一个接受任何脚本数组的实用程序函数:

$.getMultiScripts = function(arr, path) {
    var _arr = $.map(arr, function(scr) {
        return $.getScript( (path||"") + scr );
    });

    _arr.push($.Deferred(function( deferred ){
        $( deferred.resolve );
    }));

    return $.when.apply($, _arr);
}

可以像这样使用

var script_arr = [
    'myscript1.js', 
    'myscript2.js', 
    'myscript3.js'
];

$.getMultiScripts(script_arr, '/mypath/').done(function() {
    // all scripts loaded
});

其中路径将被添加到所有脚本之前,并且也是可选的,这意味着如果数组包含完整的URL,那么也可以执行此操作,并将所有路径全部放在一起

$.getMultiScripts(script_arr).done(function() { ...

参数,错误等。

另外,请注意done回调将包含许多与传入的脚本匹配的参数,每个参数表示包含响应的数组

$.getMultiScripts(script_arr).done(function(response1, response2, response3) { ...

其中每个数组都包含[content_of_file_loaded, status, xhr_object]之类的内容。 我们通常不需要访问这些参数,因为无论如何都会自动加载脚本,并且大多数情况下done回调就是我们真正知道所有脚本都已加载的所有时间,我是只是添加它是为了完整性,并且在极少数情况下需要访问加载文件中的实际文本,或者需要访问每个XHR对象或类似的东西。

此外,如果任何脚本无法加载,将调用失败处理程序,并且不会加载后续脚本

$.getMultiScripts(script_arr).done(function() {
     // all done
}).fail(function(error) {
     // one or more scripts failed to load
}).always(function() {
     // always called, both on success and error
});

答案 1 :(得分:24)

我实现了一个简单的函数来并行加载多个脚本:

功能

function getScripts(scripts, callback) {
    var progress = 0;
    scripts.forEach(function(script) { 
        $.getScript(script, function () {
            if (++progress == scripts.length) callback();
        }); 
    });
}

用法

getScripts(["script1.js", "script2.js"], function () {
    // do something...
});

答案 2 :(得分:9)

在上一个回调中加载以下所需的脚本,如:

$.getScript('scripta.js', function()
{
   $.getScript('scriptb.js', function()
   {
       // run script that depends on scripta.js and scriptb.js
   });
});

答案 3 :(得分:8)

有时需要按特定顺序加载脚本。例如,jQuery必须在jQuery UI之前加载。此页面上的大多数示例并行(异步)加载脚本,这意味着无法保证执行顺序。如果没有排序,依赖y的脚本x如果成功加载但顺序错误,可能会中断。

我提出了一种混合方法,允许顺序加载依赖脚本+可选并行加载+ deferred objects

/*
 * loads scripts one-by-one using recursion
 * returns jQuery.Deferred
 */
function loadScripts(scripts) {
  var deferred = jQuery.Deferred();

  function loadScript(i) {
    if (i < scripts.length) {
      jQuery.ajax({
        url: scripts[i],
        dataType: "script",
        cache: true,
        success: function() {
          loadScript(i + 1);
        }
      });
    } else {
      deferred.resolve();
    }
  }
  loadScript(0);

  return deferred;
}

/*
 * example using serial and parallel download together
 */

// queue #1 - jquery ui and jquery ui i18n files
var d1 = loadScripts([
  "https://ajax.googleapis.com/ajax/libs/jqueryui/1.11.1/jquery-ui.min.js",
  "https://ajax.googleapis.com/ajax/libs/jqueryui/1.11.1/i18n/jquery-ui-i18n.min.js"
]).done(function() {
  jQuery("#datepicker1").datepicker(jQuery.datepicker.regional.fr);
});

// queue #2 - jquery cycle2 plugin and tile effect plugin
var d2 = loadScripts([
  "https://cdn.rawgit.com/malsup/cycle2/2.1.6/build/jquery.cycle2.min.js",
  "https://cdn.rawgit.com/malsup/cycle2/2.1.6/build/plugin/jquery.cycle2.tile.min.js"

]).done(function() {
  jQuery("#slideshow1").cycle({
    fx: "tileBlind",
    log: false
  });
});

// trigger a callback when all queues are complete
jQuery.when(d1, d2).done(function() {
  console.log("All scripts loaded");
});
@import url("https://ajax.googleapis.com/ajax/libs/jqueryui/1.11.4/themes/blitzer/jquery-ui.min.css");

#slideshow1 {
  position: relative;
  z-index: 1;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>

<p><input id="datepicker1"></p>

<div id="slideshow1">
  <img src="https://dummyimage.com/300x100/FC0/000">
  <img src="https://dummyimage.com/300x100/0CF/000">
  <img src="https://dummyimage.com/300x100/CF0/000">
</div>

两个队列中的脚本将并行下载,但是,每个队列中的脚本将按顺序下载,以确保有序执行。瀑布图:

waterfall chart of scripts

答案 4 :(得分:4)

我遇到了一些多脚本加载的问题,其中包含一个问题(至少在Chrome中),在Ajax成功加载后,相同的域名热门加载的脚本实际上没有运行,因为Cross Domain工作得非常好! :(

原始问题的选定答案无法可靠地运作。

经过多次迭代后,这是我对getScript的最终答案,并按照特定的严格顺序异步加载多个脚本,每个脚本加载回调选项和完成后的整体回调,在jQuery 2.1+和现代版Chrome中测试, Firefox加上被遗忘的Internet Explorer。

我的测试用例是为THREE.JS webGL渲染加载文件,然后使用传递给onComplete的匿名函数调用的间隔检查,当THREE全局变为可用时启动渲染脚本。

原型功能(getScripts)

function getScripts( scripts, onScript, onComplete )
{
    this.async = true;
    this.cache = false;
    this.data = null;
    this.complete = function () { $.scriptHandler.loaded(); };
    this.scripts = scripts;
    this.onScript = onScript;
    this.onComplete = onComplete;
    this.total = scripts.length;
    this.progress = 0;
};

getScripts.prototype.fetch = function() {
    $.scriptHandler = this;
    var src = this.scripts[ this.progress ];
    console.log('%cFetching %s','color:#ffbc2e;', src);

    $.ajax({
        crossDomain:true,
        async:this.async,
        cache:this.cache,
        type:'GET',
        url: src,
        data:this.data,
        statusCode: {
            200: this.complete
        },
        dataType:'script'
    });
};

getScripts.prototype.loaded = function () {
    this.progress++;
    if( this.progress >= this.total ) {
        if(this.onComplete) this.onComplete();
    } else {
        this.fetch();
    };
    if(this.onScript) this.onScript();
};

如何使用

var scripts = new getScripts(
    ['script1.js','script2.js','script.js'],
    function() {
        /* Optional - Executed each time a script has loaded (Use for Progress updates?) */
    },
    function () {
        /* Optional - Executed when the entire list of scripts has been loaded */
    }
);
scripts.fetch();

我发现使用Deferred(现在已弃用?),When,Success&amp;在我的试验中完成并不是100%可靠!?,因此这个函数和statusCode的使用就是例如。

如果您愿意,可能需要添加错误/失败处理行为。

答案 5 :(得分:4)

使用yepnope.jsModernizr(其中包括yepnope.js为Modernizr.load)。

<强>更新

只是为了跟进,这里有一个很好的相当于你现在使用yepnope的东西,显示了对多个脚本的依赖:

yepnope({
  load: ['script1.js', 'script2.js', 'script3.js'],
  complete: function () {
      // all the scripts have loaded, do whatever you want here
  }
});

答案 6 :(得分:2)

您可以通过尝试以下函数来使用$.when - 方法:

function loadScripts(scripts) {
  scripts.forEach(function (item, i) {
    item = $.getScript(item);
  });
  return $.when.apply($, scripts);
}

此功能将使用如下:

loadScripts(['path/to/script-a.js', 'path/to/script-b.js']).done(function (respA, respB) {
    // both scripts are loaded; do something funny
});

这是使用Promise的方式,并且开销最小。

答案 7 :(得分:2)

很棒的回答,adeneo。

我花了一些时间来弄清楚如何使你的答案更通用(这样我就可以加载一组代码定义的脚本)。所有脚本加载并执行后都会调用回调。这是我的解决方案:

    function loadMultipleScripts(scripts, callback){
        var array = [];

        scripts.forEach(function(script){
            array.push($.getScript( script ))
        });

        array.push($.Deferred(function( deferred ){
                    $( deferred.resolve );
                }));

        $.when.apply($, array).done(function(){
                if (callback){
                    callback();
                }
            });
    }

答案 8 :(得分:1)

您正在寻找的是兼容AMD的加载器(如require.js)。

http://requirejs.org/

http://requirejs.org/docs/whyamd.html

如果你查一下,有许多好的开源软件。基本上,这允许您定义代码模块,如果它依赖于其他代码模块,它将等到这些模块完成下载后再继续运行。通过这种方式,您可以异步加载10个模块,即使依赖其他几个模块运行也不会出现问题。

答案 9 :(得分:1)

此函数将确保在完全加载依赖项文件后加载文件。您只需要按顺序提供文件,记住对其他文件的依赖性。

function loadFiles(files, fn) {
    if (!files.length) {
        files = [];
    }
    var head = document.head || document.getElementsByTagName('head')[0];

    function loadFile(index) {
        if (files.length > index) {
            var fileref = document.createElement('script');
            fileref.setAttribute("type", "text/javascript");
            fileref.setAttribute("src", files[index]);
            head.appendChild(fileref);
            index = index + 1;
            // Used to call a callback function
            fileref.onload = function () {
                loadFile(index);
            }
        } else if(fn){
            fn();
        }
    }
    loadFile(0);
}

答案 10 :(得分:1)

这对我有用:

function getScripts(scripts) {
    var prArr = [];
    scripts.forEach(function(script) { 
        (function(script){
            prArr .push(new Promise(function(resolve){
                $.getScript(script, function () {
                    resolve();
                });
            }));
        })(script);
    });
    return Promise.all(prArr, function(){
        return true;
    });
}

并使用它:

var jsarr = ['script1.js','script2.js'];
getScripts(jsarr).then(function(){
...
});

答案 11 :(得分:0)

我改进了@adeneo脚本,因此它将按指定顺序加载所有脚本。它不会进行链式加载,因此速度非常快,但是如果您希望更快,请更改50毫秒的等待时间。

$.getMultiScripts = function(arr, path) {

    function executeInOrder(scr, code, resolve) {
        // if its the first script that should be executed
        if (scr == arr[0]) {
            arr.shift();
            eval(code);
            resolve();
            console.log('executed', scr);
        } else {
            // waiting
            setTimeout(function(){
                executeInOrder(scr, code, resolve);
            }, 50);
        }
    }

    var _arr = $.map(arr, function(scr) {

        return new Promise((resolve) => {
            jQuery.ajax({
                type: "GET",
                url: (path || '') + scr,
                dataType: "text",
                success: function(code) {
                    console.log('loaded  ', scr);
                    executeInOrder(scr, code, resolve);
                },
                cache: true
            });
        });

    });
        
    _arr.push($.Deferred(function( deferred ){
        $( deferred.resolve );
    }));
        
    return $.when.apply($, _arr);
}

用法相同:

var script_arr = [
    'myscript1.js', 
    'myscript2.js', 
    'myscript3.js'
];

$.getMultiScripts(script_arr, '/mypath/').done(function() {
    // all scripts loaded
});

答案 12 :(得分:0)

您可以尝试使用递归。这将同步下载它们,一次又一次,直到完成下载整个列表为止。

var queue = ['url/links/go/here'];

ProcessScripts(function() { // All done do what ever you want

}, 0);

function ProcessScripts(cb, index) {
    getScript(queue[index], function() {
        index++;
        if (index === queue.length) { // Reached the end
            cb();
        } else {
            return ProcessScripts(cb, index);
        }
    });
}

function getScript(script, callback) {
    $.getScript(script, function() {
        callback();
    });
}

答案 13 :(得分:0)

使用async = false

追加脚本

这是一种不同但非常简单的方法。 要加载多个脚本,您只需将它们附加到正文即可。

  • 异步加载它们,因为浏览器如何优化它们 页面加载
  • 按顺序执行脚本,因为这是浏览器解析HTML标记的方式
  • 不需要回调,因为脚本按顺序执行。只需添加另一个脚本,它将在其他脚本之后执行

此处有更多信息:https://www.html5rocks.com/en/tutorials/speed/script-loading/

var scriptsToLoad = [
   "script1.js", 
   "script2.js",
   "script3.js",
]; 

scriptsToLoad.forEach(function(src) {
  var script = document.createElement('script');
  script.src = src;
  script.async = false;
  document.body.appendChild(script);
});

答案 14 :(得分:0)

基于@adeneo上面的答案: 结合加载css和js文件

有任何改进建议吗?

// Usage
//$.getMultiResources(['script-1.js','style-1.css'], 'assets/somePath/')
//  .done(function () {})
//  .fail(function (error) {})
//  .always(function () {});

(function ($) {
  $.getMultiResources = function (arr, pathOptional, cache) {
    cache = (typeof cache === 'undefined') ? true : cache;
    var _arr = $.map(arr, function (src) {
      var srcpath = (pathOptional || '') + src;
      if (/.css$/i.test(srcpath)) {
        return $.ajax({
          type: 'GET',
          url: srcpath,
          dataType: 'text',
          cache: cache,
          success: function () {
            $('<link>', {
              rel: 'stylesheet',
              type: 'text/css',
              'href': srcpath
            }).appendTo('head');
          }
        });

      } else {
        return $.ajax({
          type: 'GET',
          url: srcpath,
          dataType: 'script',
          cache: cache
        });
      }
    });
    //
    _arr.push($.Deferred(function (deferred) {
      $(deferred.resolve);
    }));
    //
    return $.when.apply($, _arr);
  };
})(jQuery);

答案 15 :(得分:0)

逐个加载 n 脚本(如果第二个文件需要第一个文件,则非常有用):

(function self(a,cb,i){
    i = i || 0; 
    cb = cb || function(){};    
    if(i==a.length)return cb();
    $.getScript(a[i++],self.bind(0,a,cb,i));                    
})(['list','of','script','urls'],function(){console.log('done')});

答案 16 :(得分:0)

有一个插件可以扩展jQuery的getScript方法。允许异步和同步加载并使用jQuery的缓存机制。完全披露,我写了这个。如果您找到更好的方法,请随时提供帮助。

https://github.com/hudsonfoo/jquery-getscripts

答案 17 :(得分:0)

更低版本的Andrew Marc Newton上面的综合答案。这个不会检查状态代码是否成功,您应该这样做以避免未定义的UI行为。

这是一个讨厌的系统,我可以保证jQuery,但没有其他包含,所以我想要一个足够短的技术,如果被强制进入它,就不能将其培养成外部脚本。 (你可以通过将索引0传递给第一个“递归”调用来缩短它,但是风格习惯的强迫使我加糖。)

我还将依赖项列表分配给模块名称,因此可以将此块包含在您需要“module1”的任何位置,并且脚本和相关初始化将仅包含/运行一次(您可以记录index在回调中,看到一组有序的AJAX请求运行)

if(typeof(__loaders) == 'undefined') __loaders = {};

if(typeof(__loaders.module1) == 'undefined')
{
    __loaders.module1 = false;

    var dependencies = [];

    dependencies.push('/scripts/loadmefirst.js');
    dependencies.push('/scripts/loadmenext.js');
    dependencies.push('/scripts/loadmelast.js');

    var getScriptChain  = function(chain, index)        
    {
        if(typeof(index) == 'undefined')
            index = 0;

        $.getScript(chain[index], 
            function()
            {
                if(index == chain.length - 1)
                {
                    __loaders.module1 = true;

                    /* !!!
                        Do your initialization of dependent stuff here 
                    !!! */
                }
                else 
                    getScriptChain(chain, index + 1);
            }
        );
    };

    getScriptChain(dependencies);       
}