Come includere più file js usando il metodo jQuery $ .getScript ()


127

Sto cercando di includere dinamicamente file javascript nel mio file js. Ho fatto qualche ricerca al riguardo e ho scoperto che il metodo jQuery $ .getScript () sarebbe il modo desiderato di procedere.

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

Ma mi chiedo se questo metodo può caricare più script contemporaneamente? Il motivo per cui lo sto chiedendo è perché a volte il mio file javascript dipende da più di un file js.

Grazie in anticipo.

Risposte:


315

La risposta è

Puoi usare le promesse con getScript()e attendere fino a quando tutti gli script sono caricati, qualcosa del tipo:

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

VIOLINO

UN ALTRO FIDDLE

Nel codice sopra, aggiungere un Deferred e risolverlo all'interno $()è come inserire qualsiasi altra funzione all'interno di una chiamata jQuery, come $(func), è la stessa di

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

cioè aspetta che il DOM sia pronto, quindi nell'esempio sopra $.whenattende che vengano caricati tutti gli script e che il DOM sia pronto a causa della $.Deferredchiamata che si risolve nel callback pronto per DOM.


Per un uso più generico, una funzione utile

Una funzione di utilità che accetta qualsiasi array di script potrebbe essere creata in questo modo:

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

che può essere usato in questo modo

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

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

dove il percorso sarà anteposto a tutti gli script, ed è anche facoltativo, il che significa che se l'array contiene l'URL completo, si potrebbe fare anche questo, e tralasciare il percorso tutti insieme

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

Argomenti, errori ecc.

A parte, notare che il donecallback conterrà un numero di argomenti corrispondenti agli script passati, ogni argomento che rappresenta un array contenente la risposta

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

dove ogni array conterrà qualcosa di simile [content_of_file_loaded, status, xhr_object] . In genere non è necessario accedere a tali argomenti poiché gli script verranno caricati automaticamente in ogni caso, e la maggior parte delle volte il donecallback è tutto ciò che stiamo realmente cercando di sapere che tutti gli script sono stati caricati, lo sto solo aggiungendo per completezza e per le rare occasioni in cui è necessario accedere al testo effettivo dal file caricato o quando è necessario accedere a ciascun oggetto XHR o qualcosa di simile.

Inoltre, se uno degli script non viene caricato, verrà chiamato il gestore degli errori e gli script successivi non verranno caricati

$.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
Grazie per l'ottima risposta. Ti dispiacerebbe spiegarmi perché l'aggiunta $.Deferred(function( deferred ){$( deferred.resolve );})qui?
sozhen

11
L'oggetto differito non ha davvero nulla a che fare con la promessa che ti consente di caricare più script ed eseguire una funzione quando sono tutti fatti. Controlla semplicemente se $ () è pronto e si risolve se lo è, in altre parole verifica che il DOM sia pronto prima di eseguire qualsiasi codice, proprio come document. già, e ho trovato un buon esempio di collegamento promette di ottenere in lineaScript con la funzionalità di DOM differita nel codice e ha deciso di mantenerlo come sembrava una buona idea.
adeneo,

4
Questo perché la memorizzazione nella cache in ajax è disattivata per impostazione predefinita in jQuery, per attivarla e rimuovere la query querystring: $.ajaxSetup({ cache: true });ma ciò potrebbe influire anche su altre chiamate ajax che non si desidera memorizzare nella cache, c'è molto di più su questo nei documenti per getScript e c'è anche un po 'di howto sulla creazione di una funzione getScript memorizzata nella cache denominata cache.
adeneo,

3
Ci scusiamo per l'accettazione tardiva. A volte la tua strada non funziona ancora del tutto. Non sono sicuro di quale sia la ragione. Sto cercando di incorporare quattro file di script contemporaneamente. $.when($.getScript(scriptSrc_1), $.getScript(scriptSrc_2), $.getScript(scriptSrc_3), $.getScript(scriptSrc_4), $.Deferred(function(deferred) { $(deferred.resolve); })).done(function() { ... })
Sozhen

1
Per chiunque non riesca a farlo funzionare, assicurati che non ci siano errori di analisi nei tuoi file JS, ad esempio verifica che vengano caricati correttamente in un tag <script> prima. jQuery.getScript () considererà questi errori un caricamento non riuscito e verrà chiamato .fail () invece di .done (). @SongtaoZ.
potere del prisma lunare

29

Ho implementato una semplice funzione per caricare più script in parallelo:

Funzione

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

uso

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

1
Per qualche ragione, questo ha funzionato meglio di quello di Emanuel. È anche un'implementazione molto trasparente.
Todd,

@satnam grazie :)! la risposta migliore non funziona per alcune persone (incluso me), quindi ho pubblicato questa soluzione. E sembra molto più semplice.
Andrei,

10

Carica il seguente script necessario nel callback del precedente come:

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

1
Sicuramente questo metodo scaricherà gli script in sequenza, rallentando i tempi per l'esecuzione finale?
Jimbo,

1
Corretto, @Jimbo - per essere onesti; è preferibile all'esecuzione prematura. :)
Alastair,

Le cascate dovrebbero quasi sempre essere evitate. getScript ha una promessa ... Puoi usare $ .when per ascoltare quando le promesse si risolvono.
Roi,

@Roi e chiunque altro: se ho bisogno di caricare gli script in un ORDINE SPECIFICO, cosa c'è di fondamentalmente sbagliato in questo approccio? E quale vantaggio offre quindi l'alternativa promessa?
Ifedi Okonkwo,

@IfediOkonkwo funziona quando devono essere in serie, ma l'originale ha appena detto che vorrebbe multipli in una volta (in cui, questo sarebbe un anti-pattern). Anche allora, se li volessi in serie, scriverei una funzione di loop a cui potrei passare un array. L'annidamento profondo diventa molto veloce.
Roi,

9

A volte è necessario caricare gli script in un ordine specifico. Ad esempio, jQuery deve essere caricato prima dell'interfaccia utente di jQuery. La maggior parte degli esempi in questa pagina carica gli script in parallelo (in modo asincrono), il che significa che l'ordine di esecuzione non è garantito. Senza ordinare, script yche dipendex potrebbe interrompersi se entrambi vengono caricati correttamente ma nell'ordine sbagliato.

Propongo un approccio ibrido che consenta il caricamento sequenziale di script dipendenti + caricamento parallelo opzionale + oggetti differiti :

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

Gli script in entrambe le code verranno scaricati in parallelo, tuttavia, gli script in ciascuna coda verranno scaricati in sequenza, garantendo l'esecuzione ordinata. Grafico a cascata:

diagramma a cascata degli script


Non è necessario aggiungere il tag di script in HTML una volta che lo script è stato caricato in memoria?
Ankush Jain,

4

Usa yepnope.js o Modernizr (che include yepnope.js come Modernizr.load).

AGGIORNARE

Solo per dare un'occhiata, ecco un buon equivalente di quello che hai attualmente usando yepnope, mostrando dipendenze su più script:

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

Grazie. Posso usare multiplo yepnope.injectJs( scriptSource )all'inizio del mio file javascript proprio come includere i <script>tag nel file html?
sozhen

È nuovo e, onestamente, non capisco perché sia ​​necessario. Segui la documentazione un po 'oltre l'elenco delle modifiche recenti e vedrai l'utilizzo più convenzionale.
Chris Pratt,

+1; vale la pena notare che questo gestisce correttamente le dipendenze (cioè non caricherà lo stesso script due volte) a differenza $.getScript. Questo è un grosso problema.
Ov

yepnope.js è ora obsoleto .
Stephan,

4

Mi sono imbattuto in una serie di problemi con il caricamento di più script che causava un problema con (almeno in Chrome) il caricamento a caldo dello stesso dominio di script che non era effettivamente in esecuzione dopo essere stato caricato con successo da Ajax dove Cross Domain funziona perfettamente! :(

La risposta selezionata alla domanda originale non funziona in modo affidabile.

Dopo molte molte iterazioni ecco la mia risposta finale a getScript e al caricamento di più script in modo asincrono in un ordine rigoroso specifico con opzione di callback caricata per script e callback globale al completamento, Testato in jQuery 2.1+ e versioni moderne di Chrome, Firefox e Internet Explorer abbandonato.

Il mio caso di test stava caricando i file per un rendering webGL THREE.JS, quindi ho avviato lo script di rendering quando TRE globali sono diventati disponibili utilizzando un controllo intervallo passato a una chiamata di funzione anonima a onComplete.

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

Come usare

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

La funzione è come è stata trovata usando Deferred (Deprecated now?), When, Success & Complete nelle mie prove NON è affidabile al 100%!?, Da qui questa funzione e l'uso di statusCode per esempio.

Se lo desideri, puoi aggiungere un comportamento di gestione degli errori / errori.


+1 per caricarli nell'ordine corretto, spesso essenziale, e qualcosa che la risposta accettata non garantirà a meno che non mi manchi qualcosa? Di seguito ho pubblicato una risposta simile più breve ma questo è un caso più generale.
Whelkaholism

2

È possibile utilizzare il $.whenmetodo provando la seguente funzione:

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

Questa funzione verrebbe utilizzata in questo modo:

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

Questo è il modo di usare Promises e avere un minimo di spese generali.


Questo garantisce che gli script vengano caricati in sequenza?
CyberMonk,

2

Ottima risposta, adeneo.

Mi ci è voluto un po 'di tempo per capire come rendere la tua risposta più generica (in modo da poter caricare una serie di script definiti dal codice). Il callback viene chiamato quando tutti gli script sono stati caricati ed eseguiti. Ecco la mia soluzione:

    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

Aggiungi script con async = false

Ecco un approccio diverso, ma super semplice. Per caricare più script puoi semplicemente aggiungerli al corpo.

  • Li carica in modo asincrono, perché è così che i browser ottimizzano il caricamento della pagina
  • Esegue gli script in ordine, perché è così che i browser analizzano i tag HTML
  • Non è necessario il callback, poiché gli script vengono eseguiti in ordine. Aggiungi semplicemente un altro script e verrà eseguito dopo gli altri script

Maggiori informazioni qui: 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);
});

1

Quello che stai cercando è un caricatore conforme AMD (come require.js).

http://requirejs.org/

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

Ci sono molti buoni open source se lo cerchi. Fondamentalmente ciò consente di definire un modulo di codice e, se dipende da altri moduli di codice, attenderà fino al termine del download di tali moduli prima di procedere con l'esecuzione. In questo modo è possibile caricare 10 moduli in modo asincrono e non dovrebbero esserci problemi anche se uno dipende da alcuni degli altri da eseguire.


1

Questa funzione assicurerà che un file sia caricato dopo che il file di dipendenza è stato caricato completamente. Hai solo bisogno di fornire i file in una sequenza tenendo presente le dipendenze da altri file.

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

Forse meglio della risposta accettata poiché carica i file in ordine. Se script2 richiede il caricamento di script1, l'ordinamento è essenziale.
Salman,

1

Questo funziona per me:

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

E usalo:

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

1

Ecco la risposta usando quella di Maciej Sawicki e implementandola Promisecome callback:

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

Uso:

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

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

Tutti i file Javascript vengono scaricati al più presto ed eseguiti nell'ordine indicato. Quindi taskNeedingParametersviene chiamato.


0

Versione più breve della risposta completa di Andrew Marc Newton sopra. Questo non controlla il codice di stato per il successo, cosa che dovresti fare per evitare comportamenti UI indefiniti.

Questo era per un sistema fastidioso in cui avrei potuto garantire jQuery ma nessun altro include, quindi volevo una tecnica abbastanza breve da non essere inserita in uno script esterno se forzata. (Potresti renderlo ancora più breve passando l'indice 0 alla prima chiamata "ricorsiva" ma la forza delle abitudini di stile mi ha fatto aggiungere lo zucchero).

Sto anche assegnando l'elenco delle dipendenze al nome di un modulo, quindi questo blocco può essere incluso ovunque sia necessario "module1" e gli script e l'inizializzazione dipendente saranno inclusi / eseguiti una sola volta (è possibile accedere indexal callback e vedere un singolo ordine set di richieste AJAX in esecuzione)

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

C'è un plugin là fuori che estende il metodo getScript di jQuery. Consente il caricamento asincrono e sincrono e utilizza il meccanismo di memorizzazione nella cache di jQuery. Divulgazione completa, ho scritto questo. Non esitate a contribuire se trovate un metodo migliore.

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


0

Carica n script uno per uno (utile se ad esempio il 2o file richiede il 1o):

(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

basato sulla risposta di @adeneo sopra: Combina sia il caricamento di file css che js

qualche suggerimento per miglioramenti ??

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

Puoi provarlo usando la ricorsione. Questo li scaricherà in sincronia, uno dopo l'altro fino a quando non completa il download dell'intero elenco.

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();
    });
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.