Как включить несколько js файлов с помощью метода jQuery $.getScript()

Я пытаюсь динамически включать файлы 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.

Спасибо заранее.

Ответ 1

Ответ

Вы можете использовать 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

ДРУГОЙ FIDDLE

В приведенном выше коде добавление Отложенного и его разрешение внутри $() похоже на размещение любой другой функции внутри вызова 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
});

Ответ 2

Я реализовал простую функцию для одновременного загрузки нескольких скриптов:

Функции

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

Ответ 3

Загрузите следующую требуемую script в обратном вызове предыдущей, например:

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

Ответ 4

Иногда необходимо загружать скрипты в определенном порядке. Например, 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>

Ответ 5

Используйте yepnope.js или Modernizr ( который включает yepnope.js как Modernizr.load).

UPDATE

Просто для продолжения, здесь хороший эквивалент того, что у вас в настоящее время используется yepnope, показывающее зависимости от нескольких сценариев:

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

Ответ 6

Я столкнулся с несколькими проблемами при загрузке multi script, включающей одну проблему (по крайней мере, в Chrome), такую ​​же самую горячую загрузку сценариев скриптов, которые на самом деле не выполнялись после успешной загрузки Ajax, где Cross Domain работает отлично!: (

Выбранный ответ на исходный вопрос не работает надежно.

После многих многих итераций вот мой окончательный ответ на getScript и загрузка асинхронно нескольких сценариев в определенном строгом порядке с помощью загружаемой функции обратного вызова script и общего обратного вызова при завершении, протестированного в jQuery 2.1+ и современных версиях Chrome, Firefox и оставленный Internet Explorer.

Мой тестовый файл загружал файлы для рендеринга web-рендеринга THREE.JS, затем начал рендеринг script, когда THREE global стал доступен с помощью проверки интервалов, переданной анонимному вызову функции 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();

Функция такая же, как и у меня, с использованием "Отложенные" ( "Устаревшие сейчас?" ), "Когда", "Успех" и "Завершить" в моих тестах, чтобы НЕ быть на 100% надежным! ". Следовательно, эта функция и использование кода состояния, например.

Возможно, вы захотите добавить поведение с ошибкой/сбоем, если хотите.

Ответ 7

Вы можете использовать метод $.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 и иметь минимум накладных расходов.

Ответ 8

Отличный ответ, 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();
                }
            });
    }

Ответ 9

То, что вы ищете, - это совместимый с AMD загрузчик (например, require.js).

http://requirejs.org/

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

Есть много хороших версий с открытым исходным кодом, если вы посмотрите. В основном это позволяет вам определить модуль кода, и если он зависит от других модулей кода, он будет ждать, пока эти модули не закончат загрузку, прежде чем приступить к работе. Таким образом вы можете загружать 10 модулей асинхронно и не должно быть проблем, даже если каждый из них зависит от нескольких других, которые будут выполняться.

Ответ 10

Эта функция будет следить за загрузкой файла после полной загрузки файла зависимостей. Вам просто нужно предоставить файлы в последовательности, имея в виду зависимости от других файлов.

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

Ответ 11

Это работает для меня:

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

Ответ 12

Вот ответ с использованием Maciej Sawicki one и реализации 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.

Ответ 13

Более короткая версия всеобъемлющего ответа Эндрю Марка Ньютона. Этот код не проверяет код состояния для успеха, который вы должны сделать, чтобы избежать поведения пользователя undefined.

Это было для раздражающей системы, где я мог бы гарантировать jQuery, но ни один другой не включал, поэтому мне нужна была техника, достаточно короткая, чтобы не быть обработанной во внешнюю script, если она была принудительно включена в нее. (Вы могли бы сделать это еще короче, передав индекс 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);       
}

Ответ 14

Там есть плагин, расширяющий jQuery метод getScript. Позволяет асинхронную и синхронную загрузку и использует механизм кэширования jQuery. Полное раскрытие, я написал это. Пожалуйста, не стесняйтесь, если вы найдете лучший метод.

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

Ответ 15

Загружает n скриптов один за другим (полезно, если, например, для второго файла требуется 1-й):

(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

на основе ответа от @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);

Ответ 17

Добавить скрипты с async = false

Здесь другой, но очень простой подход. Чтобы загрузить несколько сценариев, вы можете просто добавить их в тело.

  • Загружает их асинхронно, так как браузер оптимизирует загрузка страницы
  • Выполняет сценарии в порядке, потому что то, как браузеры анализируют HTML-теги
  • Нет необходимости в обратном вызове, потому что скрипты выполняются по порядку. Просто добавьте еще один script, и он будет выполнен после других скриптов

Дополнительная информация здесь: 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);
});

Ответ 18

Вы можете попробовать это с помощью рекурсии. Это будет загружать их синхронно, один за другим, пока не завершится загрузка всего списка.

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