Skripte asynchron laden

125

Ich verwende mehrere Plugins, benutzerdefinierte Widgets und einige andere Bibliotheken von JQuery. Als Ergebnis habe ich mehrere .js- und .css-Dateien. Ich muss einen Loader für meine Site erstellen, da das Laden einige Zeit in Anspruch nimmt. Es ist schön, wenn ich den Loader anzeigen kann, bevor ich Folgendes importiere:

<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

Ich habe mehrere Tutorials gefunden, mit denen ich eine JavaScript-Bibliothek asynchron importieren kann. Zum Beispiel kann ich so etwas machen wie:

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

Aus irgendeinem Grund funktionieren die Seiten nicht, wenn ich für alle meine Dateien dasselbe mache. Ich habe so lange versucht herauszufinden, wo das Problem liegt, aber ich kann es einfach nicht finden. Zuerst dachte ich, dass es wahrscheinlich daran lag, dass einige Javascript-Funktionen von den anderen abhingen. Aber ich habe sie in der richtigen Reihenfolge mit der Timeout-Funktion geladen, als eine abgeschlossen war. Ich fuhr mit der nächsten fort und die Seite verhält sich immer noch seltsam. Zum Beispiel kann ich nicht auf Links klicken usw. Animationen funktionieren trotzdem.

Sowieso

Hier ist, was ich gedacht habe ... Ich glaube, Browser haben einen Cache, deshalb dauert es lange, die Seite zum ersten Mal zu laden und das nächste Mal ist es schnell. Ich denke also daran, meine index.html-Seite durch eine Seite zu ersetzen, die alle diese Dateien asynchron lädt. Wenn Ajax fertig ist, leiten Sie alle Dateien weiter, die auf die Seite umgeleitet werden, die ich verwenden möchte. Wenn Sie diese Seite verwenden, sollte das Laden nicht lange dauern, da die Dateien bereits im Cache des Browsers enthalten sein sollten. Auf meiner Indexseite (Seite, auf der die Dateien .js und .css asynchron geladen werden) ist es mir egal, ob Fehler angezeigt werden. Ich werde nur einen Loader anzeigen und die Seite umleiten, wenn ich fertig bin ...

Ist diese Idee eine gute Alternative? oder sollte ich weiterhin versuchen, die asynchronen Methoden zu implementieren?


BEARBEITEN

Die Art und Weise, wie ich alles asynchron lade, ist wie folgt:

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

und der seltsame Teil ist, dass die gesamte Arbeit des Stils plus alle Javascript-Funktionen. Die Seite ist jedoch aus irgendeinem Grund eingefroren ...

Tono Nam
quelle

Antworten:

176

Einige Lösungen für das asynchrone Laden:

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

Wenn Sie bereits jQuery auf der Seite haben, verwenden Sie einfach:

$.getScript(url, successCallback)* *

Darüber hinaus ist es möglich, dass Ihre Skripte geladen / ausgeführt werden, bevor das Dokument geladen ist. Dies bedeutet, dass Sie warten müssen, document.readybevor Ereignisse an die Elemente gebunden werden können.

Es ist nicht möglich, genau zu sagen, was Ihr Problem ist, ohne den Code zu sehen.

Die einfachste Lösung besteht darin, alle Ihre Skripte am unteren Rand der Seite inline zu halten, damit das Laden von HTML-Inhalten während der Ausführung nicht blockiert wird. Außerdem wird das Problem vermieden, dass jedes erforderliche Skript asynchron geladen werden muss.

Wenn Sie eine besonders ausgefallene Interaktion haben, die nicht immer verwendet wird und ein größeres Skript erfordert, kann es hilfreich sein, das Laden dieses bestimmten Skripts zu vermeiden, bis es benötigt wird (verzögertes Laden).

* Mit geladene Skripte $.getScriptwerden wahrscheinlich nicht zwischengespeichert


Für alle, die moderne Funktionen wie das PromiseObjekt verwenden können, ist die loadScriptFunktion erheblich einfacher geworden:

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

Beachten Sie, dass diese Version kein callbackArgument mehr akzeptiert , da das zurückgegebene Versprechen den Rückruf behandelt. Was früher gewesen wäre, loadScript(src, callback)wäre jetzt loadScript(src).then(callback).

Dies hat den zusätzlichen Vorteil, Fehler erkennen und behandeln zu können, zum Beispiel könnte man ...

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

... und es würde CDN-Ausfälle elegant behandeln.

zzzzBov
quelle
Ich fange gleich an, das auszuprobieren. Wenn ich diese Skripte jedoch auf eine andere Seite lade, wenn ich die Seite auf eine andere Seite umleitung und diese Seite zufällig dieselben Skripte enthält, sollte das Laden keine Zeit in Anspruch nehmen. Sie sollten im Cache sein, oder? Sollte ich diese Technik nicht stattdessen implementieren?
Tono Nam
1
Ich habe das Kind geändert und an das Head-Tag anstatt an den Body angehängt ... ..var x = document.getElementsByTagName ('head') [0]; x.appendChild (s);
Tono Nam
1
@TonoNam, Sie könnten es einfach verwenden document.head.appendChild, es gibt jedoch einige Nischenprobleme beim Hinzufügen von Skripten zu <head>; Nichts, was das Laden und Ausführen von Skripten verhindern würde.
zzzzBov
1
Ich musste t.parentNode.insertBefore verwenden (im Gegensatz zu t.parent).
Phil LaNasa
1
@MuhammadUmer: Wenn Sie ein zusätzliches Skript-Tag mit schreiben, document.writebevor das Dokument geladen wurde, wird das Skript beim Laden der Seite synchron ausgeführt und enthält kein eigenständiges asynchrones Rückrufverhalten. Wenn Sie ein Skriptelement erstellen und zur Seite hinzufügen, können Sie Ereignis-Listener hinzufügen, die asynchron ausgelöst werden. tl; dr: Es hängt davon ab, wie Sie das Skript mit JS laden.
zzzzBov
33

Das neue 'async'-Attribut von HTML5 soll den Trick machen. 'defer' wird auch in den meisten Browsern unterstützt, wenn Sie sich für IE interessieren.

async - Der HTML-Code

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

Aufschieben - Der HTML-Code

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

Bei der Analyse des neuen AdSense-Anzeigenblockcodes fiel mir das Attribut auf, und eine Suche führte mich hierher: http://davidwalsh.name/html5-async

Donald Porter
quelle
Nach meinem Verständnis dient asyn nach dem Lesen von stackoverflow.com/questions/2774373/… nicht zum Laden, sondern zum Bestimmen, wann das enthaltene Javascript ausgeführt werden soll.
JackDev
9
Das Attribut defer bewirkt, dass der Browser die Ausführung des Skripts verzögert, bis das Dokument geladen und analysiert wurde und zur Bearbeitung bereit ist. Das asynchrone Attribut bewirkt, dass der Browser das Skript so schnell wie möglich
ausführt,
1
"asyn dient nicht zum Laden, sondern zum Bestimmen, wann das enthaltene Javascript ausgeführt werden soll" Dies ist kein Widerspruch; In allen Fällen lädt der Browser das Skript so schnell wie möglich. Aber ohne async oder deferblockiert es das Rendering, während es geladen wird. Durch die Einstellung asyncwird angewiesen, das Skript nicht so schnell wie möglich zu blockieren und auszuführen. Die Einstellung deferweist es an, nicht zu blockieren, sondern mit dem Ausführen des Skripts zu warten , bis die Seite fertig ist. Normalerweise ist es kein Problem, Skripte so schnell wie möglich auszuführen, solange sie keine Abhängigkeiten aufweisen (z. B. jQuery). Wenn ein Skript von einem anderen abhängig ist onLoad, warten Sie mit.
Stijn de Witt
@StijndeWitt Was ist, wenn weder asynchron noch verzögert angegeben wird?
Mohammed Shareef C
@MohammedShareefC Wenn Sie keines von beiden angeben, erhalten Sie etwas, das dem Synchronisierungsverhalten sehr ähnlich ist. Der Browser blockiert die weitere Verarbeitung, während er das Skript herunterlädt und ausführt. Das Standardverhalten ist also effektiv die Synchronisierung. Da dies jedoch die Leistung beeinträchtigt, werden die Browser so viel wie möglich daran arbeiten. Sie können mit dem Parsen, Laden und Anwenden von CSS usw. fortfahren. Sie können jedoch nicht mit der Ausführung des nächsten Skriptfragments beginnen. Also wird das Zeug blockieren. Wenn Sie Async einstellen, teilen Sie dem Browser mit, dass es in Ordnung ist, fortzufahren, und Sie stellen sicher, dass es selbst funktioniert.
Stijn de Witt
8

Ich habe die Skripte asynchron geladen (HTML 5 hat diese Funktion), als alle Skripte geladen wurden. Ich habe die Seite nach index2.html umgeleitet, wobei index2.html dieselben Bibliotheken verwendet. Da Browser einen Cache haben, sobald die Seite zu index2.html umgeleitet wird, wird index2.html in weniger als einer Sekunde geladen, da alles vorhanden ist, was zum Laden der Seite erforderlich ist. Auf meiner index.html-Seite lade ich auch die Bilder, die ich verwenden möchte, damit der Browser diese Bilder in den Cache legt. meine index.html sieht also so aus:

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

Eine weitere nette Sache ist, dass ich möglicherweise einen Loader in die Seite einfügen kann. Wenn die Seite fertig geladen ist, verschwindet der Loader und in wenigen Millisekunden wird die neue Seite ausgeführt.

Tono Nam
quelle
1
@MohammedShareefC Es ist Ironie. Eine spezielle Seite, nur um Dinge vorzuladen, klingt in Bezug auf SEO nicht nach einer guten Idee. Das Vorladen ist großartig, aber Sie müssen nicht umleiten, um es zu erreichen. Stecken Sie einfach die Links in ein verstecktes Div und der Browser kümmert sich automatisch darum. Dann nicht umleiten, sondern nur die eigentliche Seite rendern. COPY-PASTERS LESEN SIE DIESES : Bitte ändern Sie die ISO-88591-Codierung in UTF-8, bevor Sie diesen HTML-Code verwenden, damit der Rest von uns bald aus der Codierungshölle herauskommt. Vielen Dank!
Stijn de Witt
Bitte schauen Sie sich meine neue Antwort an: stackoverflow.com/questions/7718935/load-scripts-asynchronously/…
asmmahmud
8

Beispiel von 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>
Kernel Med
quelle
8

Mehrere Anmerkungen:

  • s.async = trueist nicht sehr korrekt für HTML5-Doctype, richtig ist s.async = 'async'(tatsächlich ist die Verwendung true korrekt , dank amn, der im Kommentar unten darauf hingewiesen hat )
  • Die Verwendung von Zeitüberschreitungen zur Steuerung der Bestellung ist nicht sehr gut und sicher, und Sie erhöhen auch die Ladezeit erheblich, um die Summe aller Zeitüberschreitungen zu erreichen!

Da es in letzter Zeit einen Grund gibt, Dateien asynchron zu laden, würde ich in der richtigen Reihenfolge eine etwas funktionalere Vorgehensweise gegenüber Ihrem Beispiel empfehlen ( console.logfür die Verwendung in der Produktion entfernen :)):

(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);
})();
shaman.sir
quelle
4
s.async = trueist richtig . Die Eigenschaft asyncwird in der HTML 5-Empfehlung von W3C unter w3.org/TR/html5/scripting-1.html#attr-script-async explizit als Boolescher Wert angegeben . Sie verwechseln das async Attribut mit der asyncEigenschaft, die von Objekten verfügbar gemacht wird, die die HTMLScriptElementDOM-Schnittstelle implementieren . In der Tat sollten Sie beim Bearbeiten des entsprechenden Attributs des Elements durch etwas wie Element.setAttributeverwenden, element.setAttribute("async", "async")da alle HTML-Attribute in erster Linie Text sind.
Amn
"Da es in letzter Zeit einen Grund gibt, Dateien asynchron zu laden, würde ich jedoch in der richtigen Reihenfolge einen etwas funktionsorientierteren Weg empfehlen als [...]". Ich weiß, dass Async normalerweise "besser" ist, aber auf welchen "jüngsten" Grund beziehen Sie sich?
BluE
Ich habe 'if (! Files.length) complete () geändert;' Hinzufügen einer 'Rückgabe': 'if (! files.length) {complete (); return;} '
Fil
4

Dank HTML5 können Sie jetzt die Skripte deklarieren, die Sie asynchron laden möchten, indem Sie dem Tag "async" hinzufügen:

<script async>...</script>

Hinweis: Das asynchrone Attribut gilt nur für externe Skripte (und sollte nur verwendet werden, wenn das src-Attribut vorhanden ist).

Hinweis: Es gibt verschiedene Möglichkeiten, wie ein externes Skript ausgeführt werden kann:

  • Wenn Async vorhanden ist: Das Skript wird asynchron mit dem Rest der Seite ausgeführt (das Skript wird ausgeführt, während die Seite das Parsen fortsetzt).
  • Wenn Async nicht vorhanden ist und Defer vorhanden ist: Das Skript wird ausgeführt, wenn die Analyse der Seite abgeschlossen ist
  • Wenn weder asynchron noch verzögert vorhanden ist: Das Skript wird sofort abgerufen und ausgeführt, bevor der Browser die Seite weiter analysiert

Siehe dies: http://www.w3schools.com/tags/att_script_async.asp

odroz
quelle
2

Ich würde die Antwort von zzzzBov mit einer Überprüfung auf das Vorhandensein eines Rückrufs vervollständigen und die Übergabe von Argumenten zulassen:

    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);
    }
Tibo
quelle
2

Hier ist eine großartige zeitgemäße Lösung für das Laden von asynchronen Skripten, obwohl das js-Skript nur mit async false behandelt wird .

Es gibt einen großartigen Artikel in www.html5rocks.com - Tauchen Sie tief in das trübe Wasser des Ladens von Skripten ein .

Nach Prüfung vieler möglicher Lösungen kam der Autor zu dem Schluss, dass das Hinzufügen von JS-Skripten am Ende des Body-Elements der beste Weg ist, um das Blockieren des Seiten-Renderings durch JS-Skripte zu vermeiden.

In der Zwischenzeit fügte der Autor eine weitere gute alternative Lösung für diejenigen Personen hinzu, die unbedingt Skripte asynchron laden und ausführen möchten.

Wenn Sie vier Skripte mit dem Namen haben script1.js, script2.js, script3.js, script4.js, können Sie async = false anwenden :

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

Jetzt sagt Spec : Gemeinsam herunterladen, in der richtigen Reihenfolge ausführen, sobald alle heruntergeladen sind.

Firefox <3.6, Opera sagt: Ich habe keine Ahnung, was dieses "asynchrone" Ding ist, aber es passiert einfach so, dass ich über JS hinzugefügte Skripte in der Reihenfolge ausführe, in der sie hinzugefügt wurden.

Safari 5.0 sagt: Ich verstehe "async", verstehe es aber nicht, es mit JS auf "false" zu setzen. Ich werde Ihre Skripte ausführen, sobald sie landen, in welcher Reihenfolge auch immer.

IE <10 sagt: Keine Ahnung von "async", aber es gibt eine Problemumgehung mit "onreadystatechange".

Alles andere sagt: Ich bin dein Freund, wir werden das nach dem Buch machen.

Nun der vollständige Code mit IE <10 Workaround:

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

Ein Grund, warum Ihre Skripte so langsam geladen werden könnten, ist, dass Sie alle Ihre Skripte beim Laden der Seite wie folgt ausgeführt haben:

callMyFunctions();

anstatt:

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

Dieses zweite Skript wartet, bis der Browser Ihren gesamten Javascript-Code vollständig geladen hat, bevor er mit der Ausführung eines Ihrer Skripte beginnt, sodass der Benutzer den Eindruck hat, dass die Seite schneller geladen wurde.

Wenn Sie die Benutzererfahrung verbessern möchten, indem Sie die Ladezeit verkürzen, würde ich die Option "Ladebildschirm" nicht wählen. Meiner Meinung nach wäre das viel ärgerlicher, als wenn die Seite nur langsamer geladen würde.

Therin
quelle
1

Ich würde vorschlagen, dass Sie sich Modernizr ansehen . Es ist eine kleine, leichte Bibliothek, mit der Sie Ihr Javascript asynchron laden können. Mit Funktionen können Sie überprüfen, ob die Datei geladen ist, und das Skript in der von Ihnen angegebenen ausführen.

Hier ist ein Beispiel für das Laden von 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'
  }
]);
Shawn Mclean
quelle
1

Ich habe einen kleinen Beitrag geschrieben, um Ihnen dabei zu helfen. Weitere Informationen finden Sie hier https://timber.io/snippets/asynchronously-load-a-script-in-the-browser-with-javascript/ , aber ich habe angehängt die Helferklasse unten. Es wartet automatisch darauf, dass ein Skript geladen wird, und gibt anschließend ein angegebenes Fensterattribut zurück.

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

Die Verwendung ist wie folgt:

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()
Zach
quelle
1

Vielleicht finden Sie diesen Wiki-Artikel interessant: http://ajaxpatterns.org/On-Demand_Javascript

Es wird erklärt, wie und wann eine solche Technik anzuwenden ist.

tereško
quelle
Leider scheint der Link tot zu sein. :(
Eric Seastrand
@ Eric ist, weil das eine alte Methode zum dynamischen Laden von Code ist: D
tereško
0

Nun, x.parentNodegibt das HEAD - Element, so dass Sie das Skript einfügen kurz vor dem Head - Tag. Vielleicht ist das das Problem.

Versuchen Sie es x.parentNode.appendChild()stattdessen.

Alekop
quelle
0

Überprüfen Sie diese https://github.com/stephen-lazarionok/async-resource-loader . Es gibt ein Beispiel, das zeigt, wie JS, CSS und mehrere Dateien mit einem Schuss geladen werden.

Stephen L.
quelle
Es ist möglich, es ein wenig zu ändern, um jquery Abhängigkeit loszuwerden
Stephen L.
0

Sie können LABJS oder RequreJS verwenden

Script Lader mag LABJS, RequireJSwird die Geschwindigkeit und Qualität des Codes verbessern.

Kaushik
quelle
0

Haben Sie überlegt, Fetch Injection zu verwenden? Ich habe eine Open-Source-Bibliothek namens Fetch-Inject gerollt , um solche Fälle zu behandeln. So könnte Ihr Loader mit der lib aussehen:

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

Nutzen Sie zur Erkennung der Abwärtskompatibilität die Funktionserkennung und greifen Sie auf XHR Injection- oder Script DOM-Elemente zurück oder fügen Sie die Tags einfach mit in die Seite ein document.write.

Josh Habdas
quelle
0

Hier ist meine benutzerdefinierte Lösung zur Beseitigung von JavaScript, das das Rendern blockiert:

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

Kurz gesagt, es lädt alle Ihre Skripte asynchron und führt sie anschließend aus.

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

Damjan Pavlica
quelle
1
Funktioniert nichtNo 'Access-Control-Allow-Origin' header is present on the requested resource
Abram
1
Das ist das CORS-Problem. stackoverflow.com/questions/20035101/…
Damjan Pavlica
0

Hier eine kleine ES6-Funktion, wenn jemand sie zum Beispiel in React verwenden möchte

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

JoxieMedina
quelle
-3

Ich würde vorschlagen, zuerst die Dateien zu minimieren und zu prüfen, ob Sie dadurch einen ausreichend großen Geschwindigkeitsschub erhalten. Wenn Ihr Host langsam ist, können Sie versuchen, diesen statischen Inhalt auf einem CDN abzulegen.

Jason Miesionczek
quelle