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


127

我正在尝试将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文件。

先感谢您。

Answers:


315

答案是

您可以将promise与一起使用,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
    
});

小提琴

ANOTHER FIDDLE

在上面的代码中,添加Deferred并将其解析在内部$()就像将其他函数放在jQuery调用中一样$(func),就像

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

也就是说,它等待DOM准备就绪,因此在上面的示例中$.when,由于调用在DOM ready回调中解析,因此等待所有脚本加载等待$.DeferredDOM准备就绪。


对于更通用的用途,方便的功能

可以创建一个接受任何脚本数组的实用程序函数,如下所示:

$.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
});

11
感谢您的出色回答。您能解释一下为什么$.Deferred(function( deferred ){$( deferred.resolve );})在这里添加吗?
sozhen 2012年

11
延迟的对象与诺言没有任何关系,诺言使您可以加载多个脚本并在完成后执行功能。它只是检查$()是否准备就绪,然后解决它是否存在的问题,换句话说,它会在执行任何代码之前检查DOM是否准备就绪,就像document.ready一样,我找到了一个很好的附加示例承诺使getScript在线在代码中具有延迟的DOM就绪功能,并决定保留它,因为听起来不错。
2012年

4
这是因为默认情况下,jQuery中ajax的缓存处于关闭状态,要打开它并删除querystring do:$.ajaxSetup({ cache: true });但这也可能会影响您不想缓存的其他ajax调用,因此在docs文档中对此有很多介绍getScript,甚至还有一点技巧来创建一个称为cachedScript的缓存getScript函数。
2012年

3
对不起,迟到了。有时,您的方法仍然无法正常工作。不知道是什么原因。我正在尝试一次合并四个脚本文件。$.when($.getScript(scriptSrc_1), $.getScript(scriptSrc_2), $.getScript(scriptSrc_3), $.getScript(scriptSrc_4), $.Deferred(function(deferred) { $(deferred.resolve); })).done(function() { ... })
sozhen 2012年

1
对于无法执行此操作的任何人,请确保您的JS文件中没有解析错误,即首先测试它们是否正确加载到<script>标记中。jQuery.getScript()将这些错误视为失败的加载,并且将调用.fail()而不是.done()。@SongtaoZ。
月亮棱镜功率

29

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

功能

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...
});

1
出于某种原因,这个工具比伊曼纽尔的工具要好。这也是一个非常透明的实现。
2014年

@satnam谢谢:)!最佳答案对某些人(包括我)不起作用,因此我发布了此解决方案。而且看起来更简单。
Andrei

10

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

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

1
当然,该方法将按顺序下载脚本,从而减慢了最终执行时间吗?
Jimbo,2014年

1
正确,@ Jimbo-公平地说;最好是过早执行。:)
Alastair 2014年

几乎应该避免瀑布。getScript带有一个承诺...您可以使用$ .when来监听承诺何时解决。
Roi 2015年

@Roi和其他任何人:如果我确实需要将脚本加载到特定命令中,那么从根本上讲,这种方法有什么问题?因此,承诺替代带来了什么好处?
Ifedi Okonkwo

@IfediOkonkwo在需要串联时确实可以使用,但是orig操作员刚刚提到他想要一次乘以倍数(其中这将是反模式)。即使这样,如果我要串联使用它们,我也会编写一个循环函数,可以将数组传递给它。深层嵌套会很快变得混乱。
2016年

9

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

我提出了一种混合方法,该方法允许顺序加载相关脚本+可选并行加载+ 延迟对象

/*
 * 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>

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

脚本瀑布图


一旦脚本加载到内存中,我们是否不需要在HTML中添加脚本标签?
Ankush Jain

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
  }
});

谢谢。我可以yepnope.injectJs( scriptSource )在javascript文件的开头使用多个字符,就像<script>在html文件中包含标签一样吗?
sozhen 2012年

这是新的,老实说,我不明白为什么需要它。将文档从最近的更改列表中移出一点,您将看到更常规的用法。
克里斯·普拉特

+1; 值得注意的是,这与正确管理依赖关系(即不会两次加载相同的脚本)不同$.getScript。这是一个大问题。
2013年

yepnope.js现在已弃用
斯蒂芬

4

我遇到了多个脚本加载问题,其中一个问题是(至少在Chrome中)由Ajax成功加载后(至少在Chrome中)相同域的脚本热加载实际上并未运行,因为Cross Domain可以很好地工作!:(

所选原始问题的答案无法可靠运行。

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

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

原型函数(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(现在是否已弃用?),我的试用中的何时,成功和完成都不是100%可靠的!一样,因此,例如,此功能和使用statusCode。

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


+1以正确的顺序(通常是必不可少的)加载它们,除非我遗漏了某些东西,否则可接受的答案将无法保证。我在下面发布了一个类似的简短答案,但这是一个更普遍的情况。
Whelkaholism 2014年

2

您可以$.when通过尝试以下功能来使用-method:

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
});

这就是使用Promises并减少开销的方式。


这样可以确保脚本按顺序加载吗?
Cyber​​Monk 2014年

2

好的答案,阿代内奥。

我花了一些时间来弄清楚如何使您的答案更通用(以便我可以加载一系列代码定义的脚本)。加载并执行所有脚本后,将调用回调。这是我的解决方案:

    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();
                }
            });
    }

2

用async = false附加脚本

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

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

此处提供更多信息:https : //www.html5rocks.com/zh-CN/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);
});

1

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

http://requirejs.org/

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

如果您查找的话,有很多好的开源软件。基本上,这使您可以定义代码模块,并且如果它依赖于其他代码模块,它将等待这些模块完成下载后再继续运行。这样,您可以异步加载10个模块,即使一个模块依赖于其他几个模块来运行,也应该没有问题。


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);
}

也许比接受的答案更好,因为它按顺序加载文件。如果script2需要加载script1,则必须进行排序。
Salman A

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(){
...
});

1

这是使用Maciej Sawicki的答案并实现Promise为回调的答案:

function loadScripts(urls, path) {
    return new Promise(function(resolve) {
        urls.forEach(function(src, i) {

            let script = document.createElement('script');        
            script.type = 'text/javascript';
            script.src = (path || "") + src;
            script.async = false;

            // If last script, bind the callback event to resolve
            if(i == urls.length-1) {                    
                // Multiple binding for browser compatibility
                script.onreadystatechange = resolve;
                script.onload = resolve;
            }

            // Fire the loading
            document.body.appendChild(script);
        });
    });
}

用:

let JSDependencies = ["jquery.js",
                      "LibraryNeedingJquery.js",
                      "ParametersNeedingLibrary.js"];

loadScripts(JSDependencies,'JavaScript/').then(taskNeedingParameters);

所有Javascript文件都应尽快下载并以给定的顺序执行。然后taskNeedingParameters被称为。


0

以上是安德鲁·马克·牛顿的完整答案的简短版本。此代码不会检查状态代码是否成功,您应该这样做以避免未定义的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);       
}


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')});

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);

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();
    });
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.