139

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

先感谢您。

4

19 回答 19

332

答案是

您可以使用 PromisegetScript()并等待所有脚本加载完毕,例如:

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

小提琴

另一个小提琴</p>

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

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

即它等待DOM 准备好,因此在上面的示例中$.when等待所有脚本被加载并且DOM 准备好,因为$.Deferred调用在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
});
于 2012-08-03T21:18:14.873 回答
34

I implemented a simple function to load multiple scripts in parallel:

Function

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

Usage

getScripts(["script1.js", "script2.js"], function () {
    // do something...
});
于 2014-04-14T11:08:34.743 回答
11

在前一个的回调中加载后续所需的脚本,例如:

$.getScript('scripta.js', function()
{
   $.getScript('scriptb.js', function()
   {
       // run script that depends on scripta.js and scriptb.js
   });
});
于 2013-05-20T19:38:18.970 回答
9

有时需要按特定顺序加载脚本。例如 jQuery 必须在 jQuery UI 之前加载。此页面上的大多数示例并行(异步)加载脚本,这意味着无法保证执行顺序。如果没有排序,如果两者都成功加载但顺序错误,则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>

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

脚本的瀑布图

于 2015-10-23T23:03:20.763 回答
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
  }
});
于 2012-08-03T21:02:09.443 回答
4

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

原始问题的选定答案不能可靠地工作。

经过多次迭代,这里是我对 getScript(s) 的最终答案,并以特定严格的顺序异步加载多个脚本,每个脚本加载回调选项和完成时的整体回调,在 jQuery 2.1+ 和现代版本的 Chrome、Firefox 以及被抛弃的 Internet Explorer。

我的测试用例是为 THREE.JS webGL 渲染加载文件,然后在 THREE 全局可用时使用传递给 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 (现在已弃用?),何时,Success & Complete 在我的试验中不是 100% 可靠的一样!

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

于 2014-02-16T22:24:09.737 回答
2

很好的答案,adeno。

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

    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();
                }
            });
    }
于 2015-04-16T00:19:33.207 回答
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
});

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

于 2014-05-06T12:30:35.383 回答
2

这是使用 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被调用。

于 2018-05-11T12:45:14.160 回答
2

使用 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);
});
于 2017-01-25T07:57:00.877 回答
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);
}
于 2015-03-25T08:44:37.303 回答
1

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

http://requirejs.org/

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

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

于 2012-08-03T21:04:23.177 回答
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(){
...
});
于 2016-11-27T01:48:46.970 回答
0

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

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

于 2014-03-24T11:41:06.703 回答
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);       
}
于 2014-03-17T11:56:15.080 回答
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')});
于 2014-08-15T15:01:30.090 回答
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();
    });
}
于 2019-05-25T20:41:41.840 回答
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);
于 2016-06-15T13:15:27.903 回答
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
});
于 2020-11-05T15:21:34.773 回答