caricare gli script in modo asincrono


125

Sto usando diversi plugin, widget personalizzati e alcune altre librerie di JQuery. di conseguenza ho diversi file .js e .css. Devo creare un caricatore per il mio sito perché ci vuole un po 'di tempo per caricarsi. sarà bello se posso visualizzare il caricatore prima di importare tutti i:

<script type="text/javascript" src="js/jquery-1.6.2.min.js"></script>
<script type="text/javascript" src="js/myFunctions.js"></script>
<link type="text/css" href="css/main.css" rel="stylesheet" />
... 
....
 etc

Ho trovato diversi tutorial che mi consentono di importare una libreria JavaScript in modo asincrono. per esempio posso fare qualcosa come:

  (function () {
        var s = document.createElement('script');
        s.type = 'text/javascript';
        s.async = true;
        s.src = 'js/jquery-ui-1.8.16.custom.min.js';
        var x = document.getElementsByTagName('script')[0];
        x.parentNode.insertBefore(s, x);
    })();

per qualche motivo quando faccio la stessa cosa per tutti i miei file le pagine non funzionano. Ho provato per così tanto tempo a cercare di trovare dove si trova il problema ma non riesco a trovarlo. Per prima cosa ho pensato che probabilmente fosse perché alcune funzioni javascript dipendevano dalle altre. ma li ho caricati nel giusto ordine usando la funzione time out quando uno completato ho proceduto con il successivo e la pagina si comporta ancora in modo strano. ad esempio non sono in grado di fare clic sui collegamenti ecc ... le animazioni funzionano comunque ..

In ogni modo

Ecco cosa stavo pensando ... Credo che i browser abbiano una cache, ecco perché ci vuole molto tempo per caricare la pagina per la prima volta e la prossima volta è veloce. quindi quello che sto pensando di fare è sostituire la mia pagina index.html con una pagina che carica tutti questi file in modo asincrono. quando ajax ha finito di caricare tutti quei file reindirizza alla pagina che ho intenzione di usare. quando si utilizza quella pagina, il caricamento non dovrebbe richiedere molto tempo poiché i file dovrebbero già essere inclusi nella cache del browser. nella mia pagina indice (pagina in cui i file .js e .css vengono caricati in modo asincrono) non mi interessa ricevere errori. Visualizzerò solo un caricatore e reindirizzerò la pagina al termine ...

Questa idea è una buona alternativa? o devo continuare a provare a implementare i metodi asincroni?


MODIFICARE

il modo in cui carico tutto asincrono è come:

importScripts();

function importScripts()
{
    //import: jquery-ui-1.8.16.custom.min.js
    getContent("js/jquery-1.6.2.min.js",function (code) {
                var s = document.createElement('script');
                s.type = 'text/javascript';
                //s.async = true;
                s.innerHTML=code;
                var x = document.getElementsByTagName('script')[0];
                x.parentNode.insertBefore(s, x);
                setTimeout(insertNext1,1);
            });


    //import: jquery-ui-1.8.16.custom.min.js
    function insertNext1()
    {
        getContent("js/jquery-ui-1.8.16.custom.min.js",function (code) {
                    var s = document.createElement('script');
                    s.type = 'text/javascript';
                    s.innerHTML=code;
                    var x = document.getElementsByTagName('script')[0];
                    x.parentNode.insertBefore(s, x);
                    setTimeout(insertNext2,1);
                });
    }

    //import: jquery-ui-1.8.16.custom.css
    function insertNext2()
    {

        getContent("css/custom-theme/jquery-ui-1.8.16.custom.css",function (code) {
                    var s = document.createElement('link');
                    s.type = 'text/css';
                    s.rel ="stylesheet";
                    s.innerHTML=code;
                    var x = document.getElementsByTagName('script')[0];
                    x.parentNode.insertBefore(s, x);
                    setTimeout(insertNext3,1);
                });
    }

    //import: main.css
    function insertNext3()
    {

        getContent("css/main.css",function (code) {
                    var s = document.createElement('link');
                    s.type = 'text/css';
                    s.rel ="stylesheet";
                    s.innerHTML=code;
                    var x = document.getElementsByTagName('script')[0];
                    x.parentNode.insertBefore(s, x);
                    setTimeout(insertNext4,1);
                });
    }

    //import: jquery.imgpreload.min.js
    function insertNext4()
    {
        getContent("js/farinspace/jquery.imgpreload.min.js",function (code) {
                    var s = document.createElement('script');
                    s.type = 'text/javascript';
                    s.innerHTML=code;
                    var x = document.getElementsByTagName('script')[0];
                    x.parentNode.insertBefore(s, x);
                    setTimeout(insertNext5,1);
                });
    }


    //import: marquee.js
    function insertNext5()
    {
        getContent("js/marquee.js",function (code) {
                    var s = document.createElement('script');
                    s.type = 'text/javascript';
                    s.innerHTML=code;
                    var x = document.getElementsByTagName('script')[0];
                    x.parentNode.insertBefore(s, x);
                    setTimeout(insertNext6,1);
                });
    }


    //import: marquee.css
    function insertNext6()
    {

        getContent("css/marquee.css",function (code) {
                    var s = document.createElement('link');
                    s.type = 'text/css';
                    s.rel ="stylesheet";
                    s.innerHTML=code;
                    var x = document.getElementsByTagName('script')[0];
                    x.parentNode.insertBefore(s, x);
                    setTimeout(insertNext,1);
                });
    }



    function insertNext()
    {
        setTimeout(pageReadyMan,10);        
    }
}


// get the content of url and pass that content to specified function
function getContent( url, callBackFunction )
{
     // attempt to create the XMLHttpRequest and make the request
     try
     {
        var asyncRequest; // variable to hold XMLHttpRequest object
        asyncRequest = new XMLHttpRequest(); // create request object

        // register event handler
        asyncRequest.onreadystatechange = function(){
            stateChange(asyncRequest, callBackFunction);
        } 
        asyncRequest.open( 'GET', url, true ); // prepare the request
        asyncRequest.send( null ); // send the request
     } // end try
     catch ( exception )
     {
        alert( 'Request failed.' );
     } // end catch
} // end function getContent

// call function whith content when ready
function stateChange(asyncRequest, callBackFunction)
{
     if ( asyncRequest.readyState == 4 && asyncRequest.status == 200 )
     {
           callBackFunction(asyncRequest.responseText);
     } // end if
} // end function stateChange

e la parte strana è che tutto il lavoro dello stile più tutte le funzioni javascript. la pagina è bloccata per qualche motivo però ...

Risposte:


176

Un paio di soluzioni per il caricamento asincrono:

//this function will work cross-browser for loading scripts asynchronously
function loadScript(src, callback)
{
  var s,
      r,
      t;
  r = false;
  s = document.createElement('script');
  s.type = 'text/javascript';
  s.src = src;
  s.onload = s.onreadystatechange = function() {
    //console.log( this.readyState ); //uncomment this line to see which ready states are called.
    if ( !r && (!this.readyState || this.readyState == 'complete') )
    {
      r = true;
      callback();
    }
  };
  t = document.getElementsByTagName('script')[0];
  t.parentNode.insertBefore(s, t);
}

Se hai già jQuery sulla pagina, usa semplicemente:

$.getScript(url, successCallback)*

Inoltre, è possibile che i tuoi script vengano caricati / eseguiti prima che il documento sia terminato, il che significa che dovresti aspettare document.readyprima che gli eventi possano essere associati agli elementi.

Non è possibile dire in modo specifico qual è il tuo problema senza vedere il codice.

La soluzione più semplice è mantenere tutti i tuoi script in linea nella parte inferiore della pagina, in questo modo non bloccano il caricamento del contenuto HTML durante l'esecuzione. Inoltre evita il problema di dover caricare in modo asincrono ogni script richiesto.

Se hai un'interazione particolarmente elaborata che non è sempre utilizzata e richiede uno script più grande di qualche tipo, potrebbe essere utile evitare di caricare quel particolare script finché non è necessario (caricamento lento).

* gli script caricati con $.getScriptprobabilmente non verranno memorizzati nella cache


Per chiunque possa utilizzare funzionalità moderne come l' Promiseoggetto, la loadScriptfunzione è diventata notevolmente più semplice:

function loadScript(src) {
    return new Promise(function (resolve, reject) {
        var s;
        s = document.createElement('script');
        s.src = src;
        s.onload = resolve;
        s.onerror = reject;
        document.head.appendChild(s);
    });
}

Tieni presente che questa versione non accetta più un callbackargomento poiché la promessa restituita gestirà il callback. Quello che prima sarebbe stato loadScript(src, callback)ora lo sarebbe loadScript(src).then(callback).

Questo ha l'ulteriore vantaggio di poter rilevare e gestire i guasti, ad esempio si potrebbe chiamare ...

loadScript(cdnSource)
    .catch(loadScript.bind(null, localSource))
    .then(successCallback, failureCallback);

... e gestirebbe le interruzioni della CDN con grazia.


Sto per iniziare a provarlo. ma se carico quegli script su una pagina diversa quando reindirizzo la pagina a una pagina diversa e quella pagina ha gli stessi script, il caricamento non dovrebbe richiedere tempo. dovrebbero essere nella cache, giusto? quindi non dovrei implementare quella tecnica invece?
Tono Nam

1
Ho cambiato e aggiunto il bambino al tag head invece che al body ... ..var x = document.getElementsByTagName ('head') [0]; x.appendChild (s);
Tono Nam

1
@TonoNam, potresti semplicemente usare document.head.appendChild, tuttavia ci sono alcuni problemi di nicchia con l'aggiunta di script a <head>; nulla che impedirebbe il caricamento e l'esecuzione degli script.
zzzzBov

1
Ho dovuto usare t.parentNode.insertBefore (al contrario di t.parent).
Phil LaNasa

1
@MuhammadUmer, se stai scrivendo un tag di script aggiuntivo con document.writeprima che il documento sia stato caricato, lo script verrà eseguito in modo sincrono come parte del caricamento della pagina e non includerà alcun comportamento di callback asincrono da solo. Se stai creando un elemento di script e lo aggiungi alla pagina, avrai accesso per aggiungere listener di eventi da attivare in modo asincrono. tl; dr: dipende da come stai caricando lo script con JS.
zzzzBov

33

Il nuovo attributo 'async' di HTML5 dovrebbe fare il trucco. "defer" è supportato anche nella maggior parte dei browser se ti interessa IE.

async - Il codice HTML

<script async src="siteScript.js" onload="myInit()"></script>

defer - Il codice HTML

<script defer src="siteScript.js" onload="myInit()"></script>

Durante l'analisi del nuovo codice dell'unità pubblicitaria adsense ho notato l'attributo e una ricerca mi ha portato qui: http://davidwalsh.name/html5-async


dalla mia comprensione dopo aver letto stackoverflow.com/questions/2774373/... , asyn non è per il carico, ma per determinare quando per eseguire il javascript contenuta.
JackDev

9
L'attributo defer fa sì che il browser rinvii l'esecuzione dello script fino a quando il documento non è stato caricato e analizzato ed è pronto per essere manipolato. L'attributo async fa sì che il browser esegua lo script il prima possibile ma non blocchi l'analisi del documento durante il download dello script
shuseel

1
"asyn non è per il caricamento, ma per determinare quando eseguire il javascript contenuto" Questo non è contraddittorio; In tutti i casi, il browser inizierà a caricare lo script al più presto. Ma senza async o deferbloccherà il rendering durante il caricamento. L'impostazione asyncgli dirà di non bloccare ed eseguire lo script al più presto. L'impostazione defergli dirà di non bloccare ma di attendere con l' esecuzione dello script fino al termine della pagina. Di solito non è un problema eseguire gli script al più presto finché non hanno dipendenze (ad esempio jQuery). Se uno script ha delle dipendenze da un altro, usa onLoadper aspettarlo.
Stijn de Witt

@StijndeWitt E se non fosse specificato né asincrono né differire?
Mohammed Shareef C

@MohammedShareefC Se non specifichi nessuno dei due, ottieni qualcosa di molto simile al comportamento di sincronizzazione; il browser blocca l'ulteriore elaborazione mentre scarica ed esegue lo script. Quindi in modo efficace il comportamento predefinito è la sincronizzazione. Ma poiché questo è dannoso per le prestazioni, i browser lo aggireranno il più possibile. Possono andare avanti con l'analisi, il caricamento e l'applicazione di CSS ecc ... Non possono tuttavia avviare l'esecuzione del frammento di script successivo. Quindi le cose si bloccheranno. Impostando asincrono, stai dicendo al browser che va bene andare avanti e ti assicurerai di farlo funzionare da solo.
Stijn de Witt

8

Ho caricato gli script in modo asincrono (html 5 ha quella funzione) quando tutti gli script sono stati caricati ho reindirizzato la pagina a index2.html dove index2.html utilizza le stesse librerie. Poiché i browser hanno una cache una volta che la pagina reindirizza a index2.html, index2.html si carica in meno di un secondo perché ha tutto ciò di cui ha bisogno per caricare la pagina. Nella mia pagina index.html carico anche le immagini che intendo utilizzare in modo che il browser inserisca quelle immagini nella cache. quindi il mio index.html ha il seguente aspetto:

<!DOCTYPE html>
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
    <title>Project Management</title>

    <!-- the purpose of this page is to load all the scripts on the browsers cache so that pages can load fast from now on -->

    <script type="text/javascript">

        function stylesheet(url) {
            var s = document.createElement('link');
            s.type = 'text/css';
            s.async = true;
            s.src = url;
            var x = document.getElementsByTagName('head')[0];
            x.appendChild(s);
        }

        function script(url) {
            var s = document.createElement('script');
            s.type = 'text/javascript';
            s.async = true;
            s.src = url;
            var x = document.getElementsByTagName('head')[0];
            x.appendChild(s);
        }

        //load scritps to the catche of browser
        (function () {            
                stylesheet('css/custom-theme/jquery-ui-1.8.16.custom.css');
                stylesheet('css/main.css');
                stylesheet('css/marquee.css');
                stylesheet('css/mainTable.css');

                script('js/jquery-ui-1.8.16.custom.min.js');
                script('js/jquery-1.6.2.min.js');
                script('js/myFunctions.js');
                script('js/farinspace/jquery.imgpreload.min.js');
                script('js/marquee.js');            
        })();

    </script>

    <script type="text/javascript">
       // once the page is loaded go to index2.html
        window.onload = function () {
            document.location = "index2.html";
        }
    </script>

</head>
<body>

<div id="cover" style="position:fixed; left:0px; top:0px; width:100%; height:100%; background-color:Black; z-index:100;">Loading</div>

<img src="images/home/background.png" />
<img src="images/home/3.png"/>
<img src="images/home/6.jpg"/>
<img src="images/home/4.png"/>
<img src="images/home/5.png"/>
<img src="images/home/8.jpg"/>
<img src="images/home/9.jpg"/>
<img src="images/logo.png"/>
<img src="images/logo.png"/>
<img src="images/theme/contentBorder.png"/>

</body>
</html>

un'altra cosa bella di questo è che posso inserire un caricatore nella pagina e quando la pagina è terminata il caricamento il caricatore andrà via e in un mascherino di millisecondi la nuova pagina sarà in esecuzione.


1
@MohammedShareefC È ironico. Una pagina speciale solo per precaricare le cose non sembra una buona idea dal punto di vista SEO. Il precaricamento è ottimo, ma non è necessario reindirizzare per ottenerlo. Basta inserire i collegamenti in un div nascosto e il browser se ne occuperà automaticamente per te. Quindi non reindirizzare ma semplicemente eseguire il rendering della pagina effettiva. COPY-PASTER LEGGI QUESTO : Si prega di cambiare la codifica ISO-88591 in UTF-8 prima di utilizzare qualsiasi di questo HTML in modo che il resto di noi possa uscire dall'inferno della codifica in qualsiasi momento presto. Grazie!
Stijn de Witt


8

Esempio da google

<script type="text/javascript">
  (function() {
    var po = document.createElement('script'); po.type = 'text/javascript'; po.async = true;
    po.src = 'https://apis.google.com/js/plusone.js?onload=onLoadCallback';
    var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(po, s);
  })();
</script>

8

Diverse note:

  • s.async = truenon è molto corretto per doctype HTML5, corretto lo è s.async = 'async'(in realtà l'uso true è corretto , grazie ad amn che lo ha sottolineato nel commento appena sotto)
  • Usare i timeout per controllare l'ordine non è molto buono e sicuro, e si aumenta anche il tempo di caricamento, per eguagliare la somma di tutti i timeout!

Poiché c'è un motivo recente per caricare i file in modo asincrono, ma in ordine, consiglierei un modo un po 'più funzionale sul tuo esempio (rimuovi console.logper uso di produzione :)):

(function() {
    var prot = ("https:"===document.location.protocol?"https://":"http://");

    var scripts = [
        "path/to/first.js",
        "path/to/second.js",
        "path/to/third.js"
    ];

    function completed() { console.log('completed'); }  // FIXME: remove logs

    function checkStateAndCall(path, callback) {
        var _success = false;
        return function() {
            if (!_success && (!this.readyState || (this.readyState == 'complete'))) {
                _success = true;
                console.log(path, 'is ready'); // FIXME: remove logs
                callback();
            }
        };
    }

    function asyncLoadScripts(files) {
        function loadNext() { // chain element
            if (!files.length) completed();
            var path = files.shift();
            var scriptElm = document.createElement('script');
            scriptElm.type = 'text/javascript';
            scriptElm.async = true;
            scriptElm.src = prot+path;
            scriptElm.onload = scriptElm.onreadystatechange = \
                checkStateAndCall(path, loadNext); // load next file in chain when
                                                   // this one will be ready 
            var headElm = document.head || document.getElementsByTagName('head')[0];
            headElm.appendChild(scriptElm);
        }
        loadNext(); // start a chain
    }

    asyncLoadScripts(scripts);
})();

4
s.async = trueè corretto . La proprietà asyncè specificata esplicitamente come booleano nella raccomandazione HTML 5 del W3C su w3.org/TR/html5/scripting-1.html#attr-script-async . Stai confondendo l' async attributo con la asyncproprietà esposta dagli oggetti che implementano l' HTMLScriptElementinterfaccia DOM. In effetti, quando si manipola l'attributo corrispondente dell'elemento tramite qualcosa di simile Element.setAttribute, è necessario utilizzarlo element.setAttribute("async", "async")poiché tutti gli attributi HTML sono prima di tutto testo.
amn

"Poiché c'è un motivo recente per caricare i file in modo asincrono, ma in ordine, consiglierei un modo un po 'più funzionale rispetto al tuo esempio [...]". So che l'asincronia di solito è "migliore", ma a quale motivo "recente" ti riferisci?
BluE

Ho cambiato 'if (! Files.length) completed ();' aggiungendo un 'ritorno': 'if (! files.length) {completed (); return;} "
Fil

4

Grazie a HTML5, ora puoi dichiarare gli script che desideri caricare in modo asincrono aggiungendo "async" nel tag:

<script async>...</script>

Nota: l'attributo async è solo per gli script esterni (e dovrebbe essere usato solo se è presente l'attributo src).

Nota: esistono diversi modi per eseguire uno script esterno:

  • Se è presente async: lo script viene eseguito in modo asincrono con il resto della pagina (lo script verrà eseguito mentre la pagina continua l'analisi)
  • Se async non è presente e defer è presente: lo script viene eseguito quando la pagina ha terminato l'analisi
  • Se non è presente né async né defer: lo script viene recuperato ed eseguito immediatamente, prima che il browser continui ad analizzare la pagina

Vedi questo: http://www.w3schools.com/tags/att_script_async.asp


2

Completerei la risposta di zzzzBov con un controllo per la presenza di callback e consentirebbe il passaggio di argomenti:

    function loadScript(src, callback, args) {
      var s, r, t;
      r = false;
      s = document.createElement('script');
      s.type = 'text/javascript';
      s.src = src;
      if (typeof(callback) === 'function') {
        s.onload = s.onreadystatechange = function() {
          if (!r && (!this.readyState || this.readyState === 'complete')) {
            r = true;
            callback.apply(args);
          }
        };
      };
      t = document.getElementsByTagName('script')[0];
      t.parent.insertBefore(s, t);
    }

2

Ecco un'ottima soluzione contemporanea per il caricamento di script asincrono sebbene affronti solo lo script js con async false .

C'è un ottimo articolo scritto in www.html5rocks.com - Immergiti nelle acque torbide del caricamento degli script .

Dopo aver considerato molte possibili soluzioni, l'autore ha concluso che l'aggiunta di script js alla fine dell'elemento body è il miglior modo possibile per evitare di bloccare il rendering della pagina da parte di script js.

Nel frattempo, l'autore ha aggiunto un'altra buona soluzione alternativa per quelle persone che sono alla disperata ricerca di caricare ed eseguire gli script in modo asincrono.

Considerando che hai quattro script denominati script1.js, script2.js, script3.js, script4.js, puoi farlo applicando async = false :

[
  'script1.js',
  'script2.js',
  'script3.js',
  'script4.js'
].forEach(function(src) {
  var script = document.createElement('script');
  script.src = src;
  script.async = false;
  document.head.appendChild(script);
});

Ora, dice Spec : Scarica insieme, esegui in ordine non appena vengono scaricati tutti.

Firefox <3.6, Opera dice: Non ho idea di cosa sia questa cosa "asincrona", ma succede così che eseguo gli script aggiunti tramite JS nell'ordine in cui sono aggiunti.

Safari 5.0 dice: Capisco "async", ma non capisco impostarlo su "false" con JS. Eseguirò i tuoi script non appena atterrano, in qualsiasi ordine.

IE <10 dice: Nessuna idea su "async", ma c'è una soluzione alternativa utilizzando "onreadystatechange".

Tutto il resto dice: sono tuo amico, lo faremo come da manuale.

Ora, il codice completo con IE <10 soluzione alternativa:

var scripts = [
  'script1.js',
  'script2.js',
  'script3.js',
  'script4.js'
];
var src;
var script;
var pendingScripts = [];
var firstScript = document.scripts[0];

// Watch scripts load in IE
function stateChange() {
  // Execute as many scripts in order as we can
  var pendingScript;
  while (pendingScripts[0] && ( pendingScripts[0].readyState == 'loaded' || pendingScripts[0].readyState == 'complete' ) ) {
    pendingScript = pendingScripts.shift();
    // avoid future loading events from this script (eg, if src changes)
    pendingScript.onreadystatechange = null;
    // can't just appendChild, old IE bug if element isn't closed
    firstScript.parentNode.insertBefore(pendingScript, firstScript);
  }
}

// loop through our script urls
while (src = scripts.shift()) {
  if ('async' in firstScript) { // modern browsers
    script = document.createElement('script');
    script.async = false;
    script.src = src;
    document.head.appendChild(script);
  }
  else if (firstScript.readyState) { // IE<10
    // create a script and add it to our todo pile
    script = document.createElement('script');
    pendingScripts.push(script);
    // listen for state changes
    script.onreadystatechange = stateChange;
    // must set src AFTER adding onreadystatechange listener
    // else we’ll miss the loaded event for cached scripts
    script.src = src;
  }
  else { // fall back to defer
    document.write('<script src="' + src + '" defer></'+'script>');
  }
}

1

Uno dei motivi per cui i tuoi script potrebbero caricarsi così lentamente è se stessi eseguendo tutti i tuoi script durante il caricamento della pagina, in questo modo:

callMyFunctions();

invece di:

$(window).load(function() {
      callMyFunctions();
});

Questo secondo bit di script attende che il browser abbia caricato completamente tutto il codice Javascript prima di iniziare a eseguire uno qualsiasi dei tuoi script, facendo sembrare all'utente che la pagina sia stata caricata più velocemente.

Se stai cercando di migliorare l'esperienza dell'utente riducendo il tempo di caricamento, non sceglierei l'opzione "schermata di caricamento". A mio parere sarebbe molto più fastidioso che caricare la pagina più lentamente.


1

Ti suggerisco di dare un'occhiata a Modernizr . È una piccola libreria leggera che puoi caricare in modo asincrono il tuo javascript con funzionalità che ti permettono di controllare se il file è caricato ed eseguire lo script nell'altro che specifichi.

Ecco un esempio di caricamento di jquery:

Modernizr.load([
  {
    load: '//ajax.googleapis.com/ajax/libs/jquery/1.6.1/jquery.js',
    complete: function () {
      if ( !window.jQuery ) {
            Modernizr.load('js/libs/jquery-1.6.1.min.js');
      }
    }
  },
  {
    // This will wait for the fallback to load and
    // execute if it needs to.
    load: 'needs-jQuery.js'
  }
]);

1

Ho scritto un piccolo post per aiutare con questo, puoi leggere di più qui https://timber.io/snippets/asynchronously-load-a-script-in-the-browser-with-javascript/ , ma ho allegato la classe helper di seguito. Aspetterà automaticamente il caricamento di uno script e restituirà un attributo di finestra specificato una volta che lo fa.

export default class ScriptLoader {
  constructor (options) {
    const { src, global, protocol = document.location.protocol } = options
    this.src = src
    this.global = global
    this.protocol = protocol
    this.isLoaded = false
  }

  loadScript () {
    return new Promise((resolve, reject) => {
      // Create script element and set attributes
      const script = document.createElement('script')
      script.type = 'text/javascript'
      script.async = true
      script.src = `${this.protocol}//${this.src}`

      // Append the script to the DOM
      const el = document.getElementsByTagName('script')[0]
      el.parentNode.insertBefore(script, el)

      // Resolve the promise once the script is loaded
      script.addEventListener('load', () => {
        this.isLoaded = true
        resolve(script)
      })

      // Catch any errors while loading the script
      script.addEventListener('error', () => {
        reject(new Error(`${this.src} failed to load.`))
      })
    })
  }

  load () {
    return new Promise(async (resolve, reject) => {
      if (!this.isLoaded) {
        try {
          await this.loadScript()
          resolve(window[this.global])
        } catch (e) {
          reject(e)
        }
      } else {
        resolve(window[this.global])
      }
    })
  }
}

L'utilizzo è così:

const loader = new Loader({
    src: 'cdn.segment.com/analytics.js',
    global: 'Segment',
})

// scriptToLoad will now be a reference to `window.Segment`
const scriptToLoad = await loader.load()


0

Bene, x.parentNoderestituisce l'elemento HEAD, quindi stai inserendo lo script appena prima del tag head. Forse è questo il problema.

Prova x.parentNode.appendChild()invece.



0

Puoi usare LABJS o RequreJS

I caricatori di script come LABJS, RequireJSmiglioreranno la velocità e la qualità del codice.


0

Hai considerato l'utilizzo di Fetch Injection? Ho lanciato una libreria open source chiamata fetch-inject per gestire casi come questi. Ecco come potrebbe apparire il tuo caricatore usando la lib:

fetcInject([
  'js/jquery-1.6.2.min.js',
  'js/marquee.js',
  'css/marquee.css',
  'css/custom-theme/jquery-ui-1.8.16.custom.css',
  'css/main.css'
]).then(() => {
  'js/jquery-ui-1.8.16.custom.min.js',
  'js/farinspace/jquery.imgpreload.min.js'
})

Per la retrocompatibilità, sfrutta il rilevamento delle funzionalità e il fallback a XHR Injection o Script DOM Elements, o semplicemente inline i tag nella pagina utilizzando document.write.


0

Ecco la mia soluzione personalizzata per eliminare JavaScript che blocca il rendering:

// put all your JS files here, in correct order
const libs = {
  "jquery": "https://code.jquery.com/jquery-2.1.4.min.js",
  "bxSlider": "https://cdnjs.cloudflare.com/ajax/libs/bxslider/4.2.5/jquery.bxslider.min.js",
  "angular": "https://ajax.googleapis.com/ajax/libs/angularjs/1.5.0-beta.2/angular.min.js",
  "ngAnimate": "https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.0-beta.2/angular-animate.min.js"
}
const loadedLibs = {}
let counter = 0

const loadAsync = function(lib) {
  var http = new XMLHttpRequest()
  http.open("GET", libs[lib], true)
  http.onload = () => {
    loadedLibs[lib] = http.responseText
    if (++counter == Object.keys(libs).length) startScripts()
  }
  http.send()
}

const startScripts = function() {
  for (var lib in libs) eval(loadedLibs[lib])
  console.log("allLoaded")
}

for (var lib in libs) loadAsync(lib)

In breve, carica tutti i tuoi script in modo asincrono e quindi li esegue di conseguenza.

Repo Github : https://github.com/mudroljub/js-async-loader


1
Non funzionaNo 'Access-Control-Allow-Origin' header is present on the requested resource
Abram


0

Ecco una piccola funzione ES6 se qualcuno vuole usarla in React, per esempio

import {uniqueId} from 'lodash' // optional
/**
 * @param {String} file The path of the file you want to load.
 * @param {Function} callback (optional) The function to call when the script loads.
 * @param {String} id (optional) The unique id of the file you want to load.
 */
export const loadAsyncScript = (file, callback, id) => {
  const d = document
  if (!id) { id = uniqueId('async_script') } // optional
  if (!d.getElementById(id)) {
    const tag = 'script'
    let newScript = d.createElement(tag)
    let firstScript = d.getElementsByTagName(tag)[0]
    newScript.id = id
    newScript.async = true
    newScript.src = file
    if (callback) {
      // IE support
      newScript.onreadystatechange = () => {
        if (newScript.readyState === 'loaded' || newScript.readyState === 'complete') {
          newScript.onreadystatechange = null
          callback(file)
        }
      }
      // Other (non-IE) browsers support
      newScript.onload = () => {
        callback(file)
      }
    }
    firstScript.parentNode.insertBefore(newScript, firstScript)
  } else {
    console.error(`The script with id ${id} is already loaded`)
  }
}


-3

Suggerirei di esaminare prima i file e vedere se questo ti dà un aumento di velocità abbastanza grande. Se il tuo host è lento, potresti provare a mettere quel contenuto statico su una CDN.

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.