Ist es möglich, eine .js
Datei synchron aufzurufen und unmittelbar danach zu verwenden?
<script type="text/javascript">
var head = document.getElementsByTagName('head').item(0);
var script = document.createElement('script');
script.setAttribute('type', 'text/javascript');
script.setAttribute('src', 'http://mysite/my.js');
head.appendChild(script);
myFunction(); // Fails because it hasn't loaded from my.js yet.
window.onload = function() {
// Works most of the time but not all of the time.
// Especially if my.js injects another script that contains myFunction().
myFunction();
};
</script>
Dies wird vereinfacht. In meiner Implementierung befindet sich das createElement-Zeug in einer Funktion. Ich dachte darüber nach, der Funktion etwas hinzuzufügen, das überprüfen könnte, ob eine bestimmte Variable instanziiert wurde, bevor die Kontrolle zurückgegeben wird. Aber dann gibt es immer noch das Problem, was zu tun ist, wenn js von einer anderen Site eingefügt werden, über die ich keine Kontrolle habe.
Gedanken?
Bearbeiten:
Ich habe die beste Antwort für den Moment akzeptiert, weil sie eine gute Erklärung dafür gibt, was los ist. Aber wenn jemand Vorschläge hat, wie man dies verbessern kann, bin ich offen für sie. Hier ist ein Beispiel dafür, was ich tun möchte.
// Include() is a custom function to import js.
Include('my1.js');
Include('my2.js');
myFunc1('blarg');
myFunc2('bleet');
Ich möchte nur vermeiden, dass ich die Interna zu genau kennen muss, und einfach sagen können: "Ich möchte dieses Modul verwenden, und jetzt werde ich Code daraus verwenden."
quelle
eval()
jede Datei in der angegebenen Reihenfolge, sonst nur die Antwort speichern).Antworten:
Sie können Ihr
<script>
Element mit einem "Onload" -Handler erstellen. Dieser wird aufgerufen, wenn das Skript vom Browser geladen und ausgewertet wurde.var script = document.createElement('script'); script.onload = function() { alert("Script loaded and ready"); }; script.src = "http://whatever.com/the/script.js"; document.getElementsByTagName('head')[0].appendChild(script);
Sie können es nicht synchron machen.
Bearbeiten - Es wurde darauf hingewiesen, dass der IE formgetreu kein "Lade" -Ereignis auf
<script>
Tags auslöst, die geladen / ausgewertet werden. Daher würde ich annehmen, dass das nächste, was zu tun ist, das Skript mit einer XMLHttpRequest und danneval()
selbst abzurufen . (Oder ich nehme an, Sie füllen den Text in ein<script>
Tag, das Sie hinzufügen. Die Ausführungsumgebung voneval()
wird vom lokalen Bereich beeinflusst, sodass nicht unbedingt das getan wird, was Sie möchten.)Bearbeiten - Ab Anfang 2013 würde ich dringend empfehlen, ein robusteres Tool zum Laden von Skripten wie Requirejs in Betracht zu ziehen . Es gibt viele Sonderfälle, über die man sich Sorgen machen muss. Für wirklich einfache Situationen gibt es yepnope , das jetzt in Modernizr integriert ist .
quelle
eval()
. Das Debuggen ist jedoch ein Albtraum, da die Fehlermeldung meldet, dass die Zeileeval()
angezeigt wird, nicht der tatsächliche FehlerDas ist nicht schön, aber es funktioniert:
<script type="text/javascript"> document.write('<script type="text/javascript" src="other.js"></script>'); </script> <script type="text/javascript"> functionFromOther(); </script>
Oder
<script type="text/javascript"> document.write('<script type="text/javascript" src="other.js"></script>'); window.onload = function() { functionFromOther(); }; </script>
Das Skript muss entweder in einem separaten
<script>
Tag oder vorher enthalten seinwindow.onload()
.Dies wird nicht funktionieren:
<script type="text/javascript"> document.write('<script type="text/javascript" src="other.js"></script>'); functionFromOther(); // Error </script>
Dasselbe kann beim Erstellen eines Knotens wie bei Pointy durchgeführt werden, jedoch nur in FF. Sie können nicht garantieren, wann das Skript in anderen Browsern verfügbar ist.
Als XML-Purist hasse ich das wirklich. Aber es funktioniert vorhersehbar. Sie könnten diese hässlichen
document.write()
s leicht einwickeln, damit Sie sie nicht ansehen müssen. Sie können sogar Tests durchführen, einen Knoten erstellen und anhängen und dann darauf zurückgreifendocument.write()
.quelle
application/xhtml+xml
.document.write("<SCR" + "IPT>" + "...")
.<head>
die mehrere andere Abhängigkeiten (oder private Dateien) geladen werden.Dies ist viel zu spät, aber für zukünftige Hinweise auf alle, die dies tun möchten, können Sie Folgendes verwenden:
function require(file,callback){ var head=document.getElementsByTagName("head")[0]; var script=document.createElement('script'); script.src=file; script.type='text/javascript'; //real browsers script.onload=callback; //Internet explorer script.onreadystatechange = function() { if (this.readyState == 'complete') { callback(); } } head.appendChild(script); }
Ich habe vor einiger Zeit einen kurzen Blog-Beitrag dazu verfasst: http://crlog.info/2011/10/06/dynamically-requireinclude-a-javascript-file-into-a-page-and-be-notified-when-its -geladen /
quelle
Douglas Crockford ( YUI-Blog )
Okay, schnall deine Sitze an, denn es wird eine holprige Fahrt. Immer mehr Leute fragen nach dem dynamischen Laden von Skripten über Javascript, es scheint ein heißes Thema zu sein.
Die Hauptgründe, warum dies so populär wurde, sind:
Informationen zur Modularität : Es liegt auf der Hand, dass die Verwaltung clientseitiger Abhängigkeiten direkt clientseitig erfolgen sollte. Wenn ein bestimmtes Objekt, Modul oder eine Bibliothek benötigt wird, fragen wir einfach danach und laden es dynamisch.
Fehlerbehandlung : Wenn eine Ressource ausfällt, haben wir immer noch die Möglichkeit, nur die Teile zu blockieren, die vom betroffenen Skript abhängen, oder es sogar mit einer gewissen Verzögerung erneut zu versuchen.
Die Leistung ist zu einem Wettbewerbsvorteil zwischen Websites geworden. Sie ist jetzt ein Suchrankingfaktor. Dynamische Skripte können asynchrones Verhalten imitieren, im Gegensatz zu der Standardblockierungsmethode für den Umgang von Browsern mit Skripten. Skripte blockieren andere Ressourcen, Skripte blockieren das weitere Parsen des HTML-Dokuments, Skripte blockieren die Benutzeroberfläche. Mit dynamischen Skript-Tags und ihren browserübergreifenden Alternativen können Sie jetzt echte asynchrone Anforderungen ausführen und abhängigen Code nur ausführen, wenn sie verfügbar sind. Ihre Skripte werden auch mit anderen Ressourcen parallel geladen und das Rendering ist fehlerfrei.
Der Grund, warum manche Leute sich an synchrones Scripting halten, ist, dass sie daran gewöhnt sind. Sie denken, es ist der Standardweg, es ist der einfachere Weg, und einige denken vielleicht sogar, dass es der einzige Weg ist.
Das einzige, was uns wichtig ist, wenn dies in Bezug auf das Design einer Anwendung entschieden werden muss, ist die Endbenutzererfahrung . Und in diesem Bereich ist asynchron nicht zu schlagen. Der Benutzer erhält sofort Antworten (oder sagt Versprechen), und ein Versprechen ist immer besser als nichts. Ein leerer Bildschirm macht den Menschen Angst. Entwickler sollten nicht faul sein, die wahrgenommene Leistung zu verbessern .
Und zum Schluss noch ein paar Worte zur schmutzigen Seite. Was Sie tun sollten, damit es in allen Browsern funktioniert:
quelle
Die obigen Antworten haben mich in die richtige Richtung gelenkt. Hier ist eine generische Version von dem, was ich zum Laufen gebracht habe:
var script = document.createElement('script'); script.src = 'http://' + location.hostname + '/module'; script.addEventListener('load', postLoadFunction); document.head.appendChild(script); function postLoadFunction() { // add module dependent code here }
quelle
postLoadFunction()
gerufen?script.addEventListener('load', postLoadFunction);
bedeutet, dass postLoadFunction beim Laden des Skripts aufgerufen wird.Ich hatte die folgenden Probleme mit den vorhandenen Antworten auf diese Frage (und Variationen dieser Frage in anderen Stackoverflow-Threads):
Oder etwas genauer:
Meine endgültige Lösung, bei der das Skript vor der Rückkehr geladen wird und auf die alle Skripte im Debugger ordnungsgemäß zugreifen (zumindest für Chrome), lautet wie folgt:
WARNUNG: Der folgende Code sollte wahrscheinlich nur im Entwicklungsmodus verwendet werden. (Für den 'Release'-Modus empfehle ich das Vorverpacken und Minimieren OHNE dynamisches Laden von Skripten oder zumindest ohne Auswertung).
//Code User TODO: you must create and set your own 'noEval' variable require = function require(inFileName) { var aRequest ,aScript ,aScriptSource ; //setup the full relative filename inFileName = window.location.protocol + '//' + window.location.host + '/' + inFileName; //synchronously get the code aRequest = new XMLHttpRequest(); aRequest.open('GET', inFileName, false); aRequest.send(); //set the returned script text while adding special comment to auto include in debugger source listing: aScriptSource = aRequest.responseText + '\n////# sourceURL=' + inFileName + '\n'; if(noEval)//<== **TODO: Provide + set condition variable yourself!!!!** { //create a dom element to hold the code aScript = document.createElement('script'); aScript.type = 'text/javascript'; //set the script tag text, including the debugger id at the end!! aScript.text = aScriptSource; //append the code to the dom document.getElementsByTagName('body')[0].appendChild(aScript); } else { eval(aScriptSource); } };
quelle
function include(file){ return new Promise(function(resolve, reject){ var script = document.createElement('script'); script.src = file; script.type ='text/javascript'; script.defer = true; document.getElementsByTagName('head').item(0).appendChild(script); script.onload = function(){ resolve() } script.onerror = function(){ reject() } }) /*I HAVE MODIFIED THIS TO BE PROMISE-BASED HOW TO USE THIS FUNCTION include('js/somefile.js').then(function(){ console.log('loaded'); },function(){ console.log('not loaded'); }) */ }
quelle
Dies scheint eine anständige Übersicht über das Laden dynamischer Skripte zu sein: http://unixpapa.com/js/dyna.html
quelle
Ich bin es gewohnt, mehrere .js-Dateien auf meiner Website zu haben, die voneinander abhängen. Um sie zu laden und sicherzustellen, dass die Abhängigkeiten in der richtigen Reihenfolge ausgewertet werden, habe ich eine Funktion geschrieben, die alle Dateien und dann, sobald sie alle empfangen wurden, lädt
eval()
. Der Hauptnachteil ist, dass dies mit CDN nicht funktioniert. Für solche Bibliotheken (z. B. jQuery) ist es besser, sie statisch einzuschließen. Beachten Sie, dass das dynamische Einfügen von Skriptknoten in HTML nicht garantiert, dass Skripte in der richtigen Reihenfolge ausgewertet werden, zumindest nicht in Chrome (dies war der Hauptgrund für das Schreiben dieser Funktion).function xhrs(reqs) { var requests = [] , count = [] , callback ; callback = function (r,c,i) { return function () { if ( this.readyState == 4 ) { if (this.status != 200 ) { r[i]['resp']="" ; } else { r[i]['resp']= this.responseText ; } c[0] = c[0] - 1 ; if ( c[0] == 0 ) { for ( var j = 0 ; j < r.length ; j++ ) { eval(r[j]['resp']) ; } } } } } ; if ( Object.prototype.toString.call( reqs ) === '[object Array]' ) { requests.length = reqs.length ; } else { requests.length = 1 ; reqs = [].concat(reqs); } count[0] = requests.length ; for ( var i = 0 ; i < requests.length ; i++ ) { requests[i] = {} ; requests[i]['xhr'] = new XMLHttpRequest () ; requests[i]['xhr'].open('GET', reqs[i]) ; requests[i]['xhr'].onreadystatechange = callback(requests,count,i) ; requests[i]['xhr'].send(null); } }
Ich habe nicht herausgefunden, wie man Verweise auf denselben Wert erstellt, ohne ein Array zu erstellen (für die Anzahl). Ansonsten halte ich es für selbsterklärend (wenn alles geladen ist,
eval()
jede Datei in der angegebenen Reihenfolge, sonst einfach die Antwort speichern).Anwendungsbeispiel:
xhrs( [ root + '/global.js' , window.location.href + 'config.js' , root + '/js/lib/details.polyfill.min.js', root + '/js/scripts/address.js' , root + '/js/scripts/tableofcontents.js' ]) ;
quelle
Ironischerweise habe ich, was Sie wollen, aber etwas näher an dem, was Sie hatten.
Ich
load
lade Dinge dynamisch und asynchron ein, aber mit einem solchen Rückruf (mit dojo und xmlhtpprequest)dojo.xhrGet({ url: 'getCode.php', handleAs: "javascript", content : { module : 'my.js' }, load: function() { myFunc1('blarg'); }, error: function(errorMessage) { console.error(errorMessage); } });
Eine ausführlichere Erklärung finden Sie unter hier
Das Problem ist, dass der Code irgendwo entlang der Zeile ausgewertet wird. Wenn etwas mit Ihrem Code nicht stimmt, gibt die
console.error(errorMessage);
Anweisung die Zeile aneval()
, in der sich der Code befindet, und nicht den tatsächlichen Fehler. Dies ist so ein großes Problem, dass ich tatsächlich versuche, es wieder in<script>
Anweisungen umzuwandeln (siehe hier) .quelle
<script>
Tags zurückgekehrt und habe Konventionen (zusammen mit einigen Build-Paketen) verwendet, um meine Js auf eine sinnvolle Weise zu verpacken.Dies funktioniert für moderne "immergrüne" Browser, die Async / Warten und Abrufen unterstützen .
Dieses Beispiel wird ohne Fehlerbehandlung vereinfacht, um die grundlegenden Prinzipien bei der Arbeit zu zeigen.
// This is a modern JS dependency fetcher - a "webpack" for the browser const addDependentScripts = async function( scriptsToAdd ) { // Create an empty script element const s=document.createElement('script') // Fetch each script in turn, waiting until the source has arrived // before continuing to fetch the next. for ( var i = 0; i < scriptsToAdd.length; i++ ) { let r = await fetch( scriptsToAdd[i] ) // Here we append the incoming javascript text to our script element. s.text += await r.text() } // Finally, add our new script element to the page. It's // during this operation that the new bundle of JS code 'goes live'. document.querySelector('body').appendChild(s) } // call our browser "webpack" bundler addDependentScripts( [ 'https://code.jquery.com/jquery-3.5.1.slim.min.js', 'https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/js/bootstrap.min.js' ] )
quelle
webpack
... 1. Für jedes Skript wird ein gesendetnew HTTP request
, 2. Dies überprüft auch nicht die Abhängigkeiten zwischen ihnen, 3. Nicht alle Browser unterstützenasync/await
und 4. In Bezug auf die Leistung sind wir mühsam als normal. Es wäre gut, dies inhead