Laden Sie dynamisch eine JavaScript-Datei

167

Wie können Sie eine JavaScript-Datei zuverlässig und dynamisch laden? Dies kann verwendet werden, um ein Modul oder eine Komponente zu implementieren, bei deren Initialisierung die Komponente bei Bedarf alle erforderlichen JavaScript-Bibliotheksskripte dynamisch lädt.

Der Client, der die Komponente verwendet, muss nicht alle Bibliotheksskriptdateien laden (und <script>Tags manuell in seine Webseite einfügen ), die diese Komponente implementieren - nur die 'Haupt'-Komponentenskriptdatei.

Wie erreichen Mainstream-JavaScript-Bibliotheken dies (Prototype, jQuery usw.)? Führen diese Tools mehrere JavaScript-Dateien zu einer einzigen weiterverteilbaren Build-Version einer Skriptdatei zusammen? Oder laden sie zusätzliche 'Bibliotheksskripte' dynamisch?

Eine Ergänzung zu dieser Frage: Gibt es eine Möglichkeit, das Ereignis zu behandeln, nachdem eine dynamisch enthaltene JavaScript-Datei geladen wurde? Prototyp hat document.observefür dokumentweite Ereignisse. Beispiel:

document.observe("dom:loaded", function() {
  // initially hide all containers for tab content
  $$('div.tabcontent').invoke('hide');
});

Was sind die verfügbaren Ereignisse für ein Skriptelement?

Adam
quelle

Antworten:

84

Sie können dynamische Skript-Tags schreiben (mit Prototype ):

new Element("script", {src: "myBigCodeLibrary.js", type: "text/javascript"});

Das Problem hierbei ist, dass wir nicht wissen, wann die externe Skriptdatei vollständig geladen ist.

Wir wollen oft unseren abhängigen Code in der nächsten Zeile und schreiben gerne etwas wie:

if (iNeedSomeMore) {
    Script.load("myBigCodeLibrary.js"); // includes code for myFancyMethod();
    myFancyMethod(); // cool, no need for callbacks!
}

Es gibt eine clevere Möglichkeit, Skriptabhängigkeiten ohne Rückrufe einzufügen. Sie müssen das Skript einfach über eine synchrone AJAX-Anforderung abrufen und das Skript auf globaler Ebene bewerten.

Wenn Sie Prototype verwenden, sieht die Script.load-Methode folgendermaßen aus:

var Script = {
    _loadedScripts: [],
    include: function(script) {
        // include script only once
        if (this._loadedScripts.include(script)) {
            return false;
        }
        // request file synchronous
        var code = new Ajax.Request(script, {
            asynchronous: false,
            method: "GET",
            evalJS: false,
            evalJSON: false
        }).transport.responseText;
        // eval code on global level
        if (Prototype.Browser.IE) {
            window.execScript(code);
        } else if (Prototype.Browser.WebKit) {
            $$("head").first().insert(Object.extend(
                new Element("script", {
                    type: "text/javascript"
                }), {
                    text: code
                }
            ));
        } else {
            window.eval(code);
        }
        // remember included script
        this._loadedScripts.push(script);
    }
};
Aemkei
quelle
Was kann ich tun, damit es domänenübergreifend funktioniert? (Laden des Skripts von http://web.archive.org/web/20140905044059/http://www.howtocreate.co.uk/operaStu‌​ff/userjs/aagmfunctions.js)
user2284570
@ user2284570 CORS stackoverflow.com/questions/5750696/…
Ciasto piekarz
@Ciastopiekarz: Ich kontrolliere nicht web.archive.org.
user2284570
Dann müssen Sie die Daten, auf die Sie zugreifen möchten, mit einem anderen Programm kratzen und sich selbst zur Verfügung stellen
David Schumann
Was ist Ajax.Request?
Bluejayke
72

Es gibt keinen Import / Include / Require in Javascript, aber es gibt zwei Möglichkeiten, um das zu erreichen, was Sie wollen:

1 - Sie können es mit einem AJAX-Aufruf laden und dann eval verwenden.

Dies ist der einfachste Weg, aber aufgrund der Javascript-Sicherheitseinstellungen auf Ihre Domain beschränkt. Die Verwendung von eval öffnet die Tür für Fehler und Hacks.

2 - Fügen Sie ein Skript-Tag mit der Skript-URL im HTML hinzu.

Auf jeden Fall der beste Weg. Sie können das Skript sogar von einem fremden Server laden, und es ist sauber, wenn Sie den Browser-Parser zum Auswerten des Codes verwenden. Sie können das Tag im Kopf der Webseite oder am unteren Rand des Körpers platzieren.

Beide Lösungen werden hier diskutiert und veranschaulicht.

Nun gibt es ein großes Problem, über das Sie Bescheid wissen müssen. Dies bedeutet, dass Sie den Code remote laden. Moderne Webbrowser laden die Datei und führen Ihr aktuelles Skript weiter aus, da sie alles asynchron laden, um die Leistung zu verbessern.

Wenn Sie diese Tricks direkt verwenden, können Sie Ihren neu geladenen Code in der nächsten Zeile, nachdem Sie ihn zum Laden aufgefordert haben, nicht verwenden, da er noch geladen wird.

EG: my_lovely_script.js enthält MySuperObject

var js = document.createElement("script");

js.type = "text/javascript";
js.src = jsFilePath;

document.body.appendChild(js);

var s = new MySuperObject();

Error : MySuperObject is undefined

Dann laden Sie die Seite neu und treffen F5. Und es funktioniert! Verwirrend...

Was tun?

Nun, Sie können den Hack verwenden, den der Autor in dem Link vorschlägt, den ich Ihnen gegeben habe. Zusammenfassend lässt sich sagen, dass Personen, die es eilig haben, mit en event eine Rückruffunktion ausführen, wenn das Skript geladen wird. So können Sie den gesamten Code mithilfe der Remote-Bibliothek in die Rückruffunktion einfügen. Z.B :

function loadScript(url, callback)
{
    // adding the script tag to the head as suggested before
   var head = document.getElementsByTagName('head')[0];
   var script = document.createElement('script');
   script.type = 'text/javascript';
   script.src = url;

   // then bind the event to the callback function 
   // there are several events for cross browser compatibility
   script.onreadystatechange = callback;
   script.onload = callback;

   // fire the loading
   head.appendChild(script);
}

Dann schreiben Sie den Code, den Sie verwenden möchten, nachdem das Skript in eine Lambda-Funktion geladen wurde:

var myPrettyCode = function() {
    // here, do what ever you want
};

Dann führen Sie das alles aus:

loadScript("my_lovely_script.js", myPrettyCode);

OK ich habe es. Aber es ist ein Schmerz, all dieses Zeug zu schreiben.

In diesem Fall können Sie wie immer das fantastische kostenlose jQuery-Framework verwenden, mit dem Sie in einer Zeile genau dasselbe tun können:

$.getScript("my_lovely_script.js", function() {
    alert("Script loaded and executed.");
    // here you can use anything you defined in the loaded script
});
Dhaval Shah
quelle
11
Ich kann nicht glauben, wie unterschätzt diese Antwort ist. Danke dir.
Naftalimich
2
Ich wette, es liegt daran, dass die Leute nicht gerne über die erste Zeile hinaus lesen, es sei denn, es verspricht ihnen, dass sie reich werden, wenn sie "nur diesen drei geheimen Schritten zum Erfolg folgen".
Costa
Das funktioniert. Ich hoffe, dass das Teilen meiner Erfahrungen hier Ihre Zeit spart, da ich viele Stunden damit verbracht habe. Ich verwende Angular 6 und eine angewendete Vorlage (HTML, CSS, JQuery). Das Problem ist, dass die Vorlage eine JS-Datei enthält, die geladen wird, nachdem die HTML-Elemente geladen wurden, um Listener-Ereignisse anzuhängen. Diese js-Datei war nach dem Laden der eckigen App schwer auszuführen. Durch Hinzufügen zum Skript-Tag der Angular App (Angular.json) werden diese gebündelt, diese JS-Datei jedoch nach dem Laden nicht ausgeführt. Es ist zu viel Code, um ihn in Typoskript umzuschreiben, daher war dies eine große Hilfe. Nächster Kommentar Ich werde das Beispiel wegen der Länge des Kommentars setzen
Nour Lababidi
Ich habe diesen Code einfach wie folgt verwendet: ngAfterViewInit () {debugger; $ .getScript ("/ assets / js / jquery.app.js", function () {alert ("Skript geladen und ausgeführt."); // hier können Sie alles verwenden, was Sie im geladenen Skript definiert haben}); }
Nour Lababidi
Für das '$' im Winkel folgte ich diesem: stackoverflow.com/questions/32050645/…
Nour Lababidi
28

Ich habe kürzlich mit jQuery eine viel weniger komplizierte Version verwendet :

<script src="scripts/jquery.js"></script>
<script>
  var js = ["scripts/jquery.dimensions.js", "scripts/shadedborder.js", "scripts/jqmodal.js", "scripts/main.js"];
  var $head = $("head");
  for (var i = 0; i < js.length; i++) {
    $head.append("<script src=\"" + js[i] + "\"></scr" + "ipt>");
  }
</script>

Es funktionierte hervorragend in jedem Browser, in dem ich es getestet habe: IE6 / 7, Firefox, Safari, Opera.

Update: jQuery-less Version:

<script>
  var js = ["scripts/jquery.dimensions.js", "scripts/shadedborder.js", "scripts/jqmodal.js", "scripts/main.js"];
  for (var i = 0, l = js.length; i < l; i++) {
    document.getElementsByTagName("head")[0].innerHTML += ("<script src=\"" + js[i] + "\"></scr" + "ipt>");
  }
</script>
Travis
quelle
25
Das ist großartig ... es sei denn, Sie versuchen, jquery zu laden.
1
Es sieht so aus, als würde jQuery das Require-Plugin für eine zukünftige Version in jQuery Core einbinden
Adam
1
Eine noch bessere Möglichkeit, jQuery zu verwenden, ist die Verwendung $.getScript. Siehe meine Antwort.
Muhd
1
Modernizr (yepnope.js) oder lab.js sind hierfür die geeigneten Lösungen. Die Verwendung einer umfangreichen Skriptbibliothek (die zuerst geladen werden muss) ist nicht die am besten geeignete Antwort für mobile oder viele andere Situationen.
1nfiniti
2
@ MaulikGangani ältere Browser und HTML-Validatoren würden dies als Token zum Beenden des Skripts interpretieren.
Travis
20

Ich habe im Grunde das Gleiche getan wie Sie, Adam, aber mit einer geringfügigen Änderung, um sicherzustellen, dass ich an die Kopfmarke angehängt habe, um die Arbeit zu erledigen. Ich habe einfach eine Include-Funktion (Code unten) erstellt, um sowohl Skript- als auch CSS-Dateien zu verarbeiten.

Diese Funktion überprüft auch, ob das Skript oder die CSS-Datei noch nicht dynamisch geladen wurde. Es wird nicht nach handcodierten Werten gesucht, und es gab möglicherweise einen besseren Weg, dies zu tun, aber es hat den Zweck erfüllt.

function include( url, type ){
    // First make sure it hasn't been loaded by something else.
    if( Array.contains( includedFile, url ) )
        return;

    // Determine the MIME-type
    var jsExpr = new RegExp( "js$", "i" );
    var cssExpr = new RegExp( "css$", "i" );
    if( type == null )
        if( jsExpr.test( url ) )
            type = 'text/javascript';
        else if( cssExpr.test( url ) )
            type = 'text/css';

    // Create the appropriate element.
    var tag = null;
    switch( type ){
        case 'text/javascript' :
            tag = document.createElement( 'script' );
            tag.type = type;
            tag.src = url;
            break;
        case 'text/css' :
            tag = document.createElement( 'link' );
            tag.rel = 'stylesheet';
            tag.type = type;
            tag.href = url;
            break;
    }

    // Insert it to the <head> and the array to ensure it is not
    // loaded again.
    document.getElementsByTagName("head")[0].appendChild( tag );
    Array.add( includedFile, url );
}
Palehorse
quelle
Ohne mehr Kontext als diese Gurke habe ich leider keine Vorschläge. Der obige Code funktioniert wie er ist, er wurde direkt aus einem funktionierenden Projekt gezogen.
Palehorse
5
@palehorse, Mike und Muhd, haben Recht, es könnte in Ihrem Projekt funktionieren, aber das liegt daran, dass die Variablen "includeFile" und "Array" an anderer Stelle in Ihrem Projekt definiert werden müssen. Dieser Code allein wird möglicherweise nicht ausgeführt Bearbeiten Sie es besser, damit es außerhalb des Kontexts Ihres Projekts funktioniert, oder fügen Sie zumindest einen Kommentar hinzu, in dem erläutert wird, was diese undefinierten Variablen sind (Typ usw.)
user280109
14

eine weitere tolle Antwort

$.getScript("my_lovely_script.js", function(){


   alert("Script loaded and executed.");
  // here you can use anything you defined in the loaded script

 });

https://stackoverflow.com/a/950146/671046

Naveed
quelle
2
Was kann ich tun, damit es domänenübergreifend funktioniert? (Laden des Skripts von http://web.archive.org/web/20140905044059/http://www.howtocreate.co.uk/operaStu‌​ff/userjs/aagmfunctions.js)
user2284570
9

Hier ist ein Beispielcode, den ich gefunden habe ... hat jemand einen besseren Weg?

  function include(url)
  {
    var s = document.createElement("script");
    s.setAttribute("type", "text/javascript");
    s.setAttribute("src", url);
    var nodes = document.getElementsByTagName("*");
    var node = nodes[nodes.length -1].parentNode;
    node.appendChild(s);
  }
Adam
quelle
Was kann ich tun, damit es domänenübergreifend funktioniert? (Laden des Skripts von http://web.archive.org/web/20140905044059/http://www.howtocreate.co.uk/operaStu‌​ff/userjs/aagmfunctions.js)
user2284570
6

Wenn Sie jQuery bereits geladen haben, sollten Sie $ .getScript verwenden .

Dies hat gegenüber den anderen Antworten hier den Vorteil, dass Sie über eine integrierte Rückruffunktion verfügen (um sicherzustellen, dass das Skript geladen wird, bevor der abhängige Code ausgeführt wird) und das Caching steuern können.

Muhd
quelle
4

Wenn Sie ein SYNC- Skript laden möchten , müssen Sie Skripttext direkt zum HTML HEAD-Tag hinzufügen. Wenn Sie es als hinzufügen, wird ein ASYNC-Ladevorgang ausgelöst . Verwenden Sie XHR, um Skripttext synchron aus einer externen Datei zu laden. Unten ein kurzes Beispiel (es werden Teile anderer Antworten in diesem und anderen Beiträgen verwendet):

/*sample requires an additional method for array prototype:*/

if (Array.prototype.contains === undefined) {
Array.prototype.contains = function (obj) {
    var i = this.length;
    while (i--) { if (this[i] === obj) return true; }
    return false;
};
};

/*define object that will wrap our logic*/
var ScriptLoader = {
LoadedFiles: [],

LoadFile: function (url) {
    var self = this;
    if (this.LoadedFiles.contains(url)) return;

    var xhr = new XMLHttpRequest();
    xhr.onload = function () {
        if (xhr.readyState === 4) {
            if (xhr.status === 200) {
                self.LoadedFiles.push(url);
                self.AddScript(xhr.responseText);
            } else {
                if (console) console.error(xhr.statusText);
            }
        }
    };
    xhr.open("GET", url, false);/*last parameter defines if call is async or not*/
    xhr.send(null);
},

AddScript: function (code) {
    var oNew = document.createElement("script");
    oNew.type = "text/javascript";
    oNew.textContent = code;
    document.getElementsByTagName("head")[0].appendChild(oNew);
}
};

/*Load script file. ScriptLoader will check if you try to load a file that has already been loaded (this check might be better, but I'm lazy).*/

ScriptLoader.LoadFile("Scripts/jquery-2.0.1.min.js");
ScriptLoader.LoadFile("Scripts/jquery-2.0.1.min.js");
/*this will be executed right after upper lines. It requires jquery to execute. It requires a HTML input with id "tb1"*/
$(function () { alert($('#tb1').val()); });
Sielu
quelle
3

Hat jemand einen besseren Weg?

Ich denke, nur das Hinzufügen des Skripts zum Textkörper wäre einfacher als das Hinzufügen zum letzten Knoten auf der Seite. Wie wäre es damit:

function include(url) {
  var s = document.createElement("script");
  s.setAttribute("type", "text/javascript");
  s.setAttribute("src", url);
  document.body.appendChild(s);
}
Joseph Pecoraro
quelle
Was kann ich tun, damit es domänenübergreifend funktioniert? (Laden des Skripts von http://web.archive.org/web/20140905044059/http://www.howtocreate.co.uk/operaStu‌​ff/userjs/aagmfunctions.js)
user2284570
3

Ich habe noch eine andere Lösung verwendet, die ich im Internet gefunden habe ... diese befindet sich unter creativecommons und prüft, ob die Quelle enthalten war, bevor die Funktion aufgerufen wird ...

Die Datei finden Sie hier: include.js

/** include - including .js files from JS - [email protected] - 2005-02-09
 ** Code licensed under Creative Commons Attribution-ShareAlike License 
 ** http://creativecommons.org/licenses/by-sa/2.0/
 **/              
var hIncludes = null;
function include(sURI)
{   
  if (document.getElementsByTagName)
  {   
    if (!hIncludes)
    {
      hIncludes = {}; 
      var cScripts = document.getElementsByTagName("script");
      for (var i=0,len=cScripts.length; i < len; i++)
        if (cScripts[i].src) hIncludes[cScripts[i].src] = true;
    }
    if (!hIncludes[sURI])
    {
      var oNew = document.createElement("script");
      oNew.type = "text/javascript";
      oNew.src = sURI;
      hIncludes[sURI]=true;
      document.getElementsByTagName("head")[0].appendChild(oNew);
    }
  }   
} 
Pierre Spring
quelle
Was kann ich tun, damit es domänenübergreifend funktioniert? (Laden des Skripts von http://web.archive.org/web/20140905044059/http://www.howtocreate.co.uk/operaStu‌​ff/userjs/aagmfunctions.js)
user2284570
3

Ich habe gerade etwas über eine großartige Funktion in YUI 3 erfahren (zum Zeitpunkt des Schreibens in der Vorschau-Version verfügbar). Sie können problemlos Abhängigkeiten zu YUI-Bibliotheken und zu "externen" Modulen (was Sie suchen) einfügen, ohne zu viel Code: YUI Loader .

Es beantwortet auch Ihre zweite Frage bezüglich der aufgerufenen Funktion, sobald das externe Modul geladen ist.

Beispiel:

YUI({
    modules: {
        'simple': {
            fullpath: "http://example.com/public/js/simple.js"
        },
        'complicated': {
            fullpath: "http://example.com/public/js/complicated.js"
            requires: ['simple']  // <-- dependency to 'simple' module
        }
    },
    timeout: 10000
}).use('complicated', function(Y, result) {
    // called as soon as 'complicated' is loaded
    if (!result.success) {
        // loading failed, or timeout
        handleError(result.msg);
    } else {
        // call a function that needs 'complicated'
        doSomethingComplicated(...);
    }
});

Hat für mich perfekt funktioniert und hat den Vorteil, Abhängigkeiten zu verwalten. In der YUI-Dokumentation finden Sie ein Beispiel für den YUI 2-Kalender .

Kariem
quelle
Dies ist wahrscheinlich ideal, außer dass YUI nur für diese Funktionalität übermäßig groß ist.
Muhd
3

Es gibt einen neuen vorgeschlagenen ECMA-Standard namens Dynamic Import , der kürzlich in Chrome und Safari integriert wurde.

const moduleSpecifier = './dir/someModule.js';

import(moduleSpecifier)
   .then(someModule => someModule.foo()); // executes foo method in someModule
Alister
quelle
2

Die Technik, die wir bei der Arbeit verwenden, besteht darin, die Javascript-Datei mithilfe einer AJAX-Anforderung anzufordern und dann die Rückgabe auszuwerten (). Wenn Sie die Prototypbibliothek verwenden, unterstützen sie diese Funktionalität in ihrem Ajax.Request-Aufruf.

17 von 26
quelle
2

jquery hat dies für mich mit der Funktion .append () behoben - damit wurde das komplette jquery ui-Paket geladen

/*
 * FILENAME : project.library.js
 * USAGE    : loads any javascript library
 */
    var dirPath = "../js/";
    var library = ["functions.js","swfobject.js","jquery.jeditable.mini.js","jquery-ui-1.8.8.custom.min.js","ui/jquery.ui.core.min.js","ui/jquery.ui.widget.min.js","ui/jquery.ui.position.min.js","ui/jquery.ui.button.min.js","ui/jquery.ui.mouse.min.js","ui/jquery.ui.dialog.min.js","ui/jquery.effects.core.min.js","ui/jquery.effects.blind.min.js","ui/jquery.effects.fade.min.js","ui/jquery.effects.slide.min.js","ui/jquery.effects.transfer.min.js"];

    for(var script in library){
        $('head').append('<script type="text/javascript" src="' + dirPath + library[script] + '"></script>');
    }

Zu verwenden - im Kopf Ihres HTML / PHP / etc, nachdem Sie jquery.js importiert haben, würden Sie einfach diese eine Datei so einfügen, dass sie in die gesamte Bibliothek geladen wird, indem Sie sie an den Kopf anhängen ...

<script type="text/javascript" src="project.library.js"></script>
JM Design
quelle
2

Halten Sie es schön, kurz, einfach und wartbar! :]

// 3rd party plugins / script (don't forget the full path is necessary)
var FULL_PATH = '', s =
[
    FULL_PATH + 'plugins/script.js'      // Script example
    FULL_PATH + 'plugins/jquery.1.2.js', // jQuery Library 
    FULL_PATH + 'plugins/crypto-js/hmac-sha1.js',      // CryptoJS
    FULL_PATH + 'plugins/crypto-js/enc-base64-min.js'  // CryptoJS
];

function load(url)
{
    var ajax = new XMLHttpRequest();
    ajax.open('GET', url, false);
    ajax.onreadystatechange = function ()
    {
        var script = ajax.response || ajax.responseText;
        if (ajax.readyState === 4)
        {
            switch(ajax.status)
            {
                case 200:
                    eval.apply( window, [script] );
                    console.log("library loaded: ", url);
                    break;
                default:
                    console.log("ERROR: library not loaded: ", url);
            }
        }
    };
    ajax.send(null);
}

 // initialize a single load 
load('plugins/script.js');

// initialize a full load of scripts
if (s.length > 0)
{
    for (i = 0; i < s.length; i++)
    {
        load(s[i]);
    }
}

Dieser Code ist nur ein kurzes Funktionsbeispiel , das könnte zusätzliche Feature - Funktionalität für die volle Unterstützung auf allen (oder gegeben) Plattform erfordern.

tfont
quelle
2

Der Import dynamischer Module landete in Firefox 67+ .

(async () => {
   await import('./synth/BubbleSynth.js')
})()

Mit Fehlerbehandlung:

(async () => {
    await import('./synth/BubbleSynth.js').catch((error) => console.log('Loading failed' + error))
})()

Es funktioniert auch für alle Arten von Nicht-Modul-Bibliotheken. In diesem Fall ist die Bibliothek auf dem Fensterobjekt verfügbar, wie bisher, aber nur auf Anfrage, was sehr schön ist.

Beispiel mit suncalc.js : Auf dem Server muss CORS aktiviert sein , damit dies funktioniert!

(async () => {
 await import('https://cdnjs.cloudflare.com/ajax/libs/suncalc/1.8.0/suncalc.min.js')
 .then(function(){
   let times = SunCalc.getTimes(new Date(), 51.5,-0.1);
   console.log("Golden Hour today in London: " + times.goldenHour.getHours() + ':' + times.sunrise.getMinutes() + ". Take your pics!")
  })
})()

https://caniuse.com/#feat=es6-module-dynamic-import

NVRM
quelle
1

Es gibt Skripte, die speziell für diesen Zweck entwickelt wurden.

yepnope.js ist in Modernizr und lab.js integriert ist eine optimierte (aber weniger benutzerfreundliche Version).

Ich würde nicht empfehlen, dies über eine große Bibliothek wie jquery oder prototype zu tun - da einer der Hauptvorteile eines Skriptladeprogramms die Möglichkeit ist, Skripte frühzeitig zu laden, sollten Sie nicht warten müssen, bis jquery und alle Ihre dom-Elemente vor dem Laden geladen werden Führen Sie eine Überprüfung durch, um festzustellen, ob Sie ein Skript dynamisch laden möchten.

1nfiniti
quelle
1

Ich habe ein einfaches Modul geschrieben, das das Importieren / Einschließen von Modulskripten in JavaScript automatisiert. Probieren Sie es aus und ersparen Sie sich Feedback! :) Eine ausführliche Erläuterung des Codes finden Sie in diesem Blog-Beitrag: http://stamat.wordpress.com/2013/04/12/javascript-require-import-include-modules/

var _rmod = _rmod || {}; //require module namespace
_rmod.on_ready_fn_stack = [];
_rmod.libpath = '';
_rmod.imported = {};
_rmod.loading = {
    scripts: {},
    length: 0
};

_rmod.findScriptPath = function(script_name) {
    var script_elems = document.getElementsByTagName('script');
    for (var i = 0; i < script_elems.length; i++) {
        if (script_elems[i].src.endsWith(script_name)) {
            var href = window.location.href;
            href = href.substring(0, href.lastIndexOf('/'));
            var url = script_elems[i].src.substring(0, script_elems[i].length - script_name.length);
            return url.substring(href.length+1, url.length);
        }
    }
    return '';
};

_rmod.libpath = _rmod.findScriptPath('script.js'); //Path of your main script used to mark the root directory of your library, any library


_rmod.injectScript = function(script_name, uri, callback, prepare) {

    if(!prepare)
        prepare(script_name, uri);

    var script_elem = document.createElement('script');
    script_elem.type = 'text/javascript';
    script_elem.title = script_name;
    script_elem.src = uri;
    script_elem.async = true;
    script_elem.defer = false;

    if(!callback)
        script_elem.onload = function() {
            callback(script_name, uri);
        };

    document.getElementsByTagName('head')[0].appendChild(script_elem);
};

_rmod.requirePrepare = function(script_name, uri) {
    _rmod.loading.scripts[script_name] = uri;
    _rmod.loading.length++;
};

_rmod.requireCallback = function(script_name, uri) {
    _rmod.loading.length--;
    delete _rmod.loading.scripts[script_name];
    _rmod.imported[script_name] = uri;

    if(_rmod.loading.length == 0)
        _rmod.onReady();
};

_rmod.onReady = function() {
    if (!_rmod.LOADED) {
        for (var i = 0; i < _rmod.on_ready_fn_stack.length; i++){
            _rmod.on_ready_fn_stack[i]();
        });
        _rmod.LOADED = true;
    }
};

//you can rename based on your liking. I chose require, but it can be called include or anything else that is easy for you to remember or write, except import because it is reserved for future use.
var require = function(script_name) {
    var np = script_name.split('.');
    if (np[np.length-1] === '*') {
        np.pop();
        np.push('_all');
    }

    script_name = np.join('.');
    var uri = _rmod.libpath + np.join('/')+'.js';
    if (!_rmod.loading.scripts.hasOwnProperty(script_name) 
     && !_rmod.imported.hasOwnProperty(script_name)) {
        _rmod.injectScript(script_name, uri, 
            _rmod.requireCallback, 
                _rmod.requirePrepare);
    }
};

var ready = function(fn) {
    _rmod.on_ready_fn_stack.push(fn);
};

// ----- USAGE -----

require('ivar.util.array');
require('ivar.util.string');
require('ivar.net.*');

ready(function(){
    //do something when required scripts are loaded
});
stamat
quelle
1

Ich bin in all diesen Beispielen verloren, aber heute musste ich eine externe .js von meiner Haupt-.js laden und ich tat dies:

document.write("<script src='https://www.google.com/recaptcha/api.js'></script>");
adrianTNT
quelle
Sie können einen Blick auf den Link werfen: Antwort
Asmmahmud
1

Hier ist eine einfache mit Rückruf- und IE-Unterstützung:

function loadScript(url, callback) {

    var script = document.createElement("script")
    script.type = "text/javascript";

    if (script.readyState) { //IE
        script.onreadystatechange = function () {
            if (script.readyState == "loaded" || script.readyState == "complete") {
                script.onreadystatechange = null;
                callback();
            }
        };
    } else { //Others
        script.onload = function () {
            callback();
        };
    }

    script.src = url;
    document.getElementsByTagName("head")[0].appendChild(script);
}

loadScript("https://ajax.googleapis.com/ajax/libs/jquery/1.6.1/jquery.min.js", function () {

     //jQuery loaded
     console.log('jquery loaded');

});
Jacob
quelle
1

Ich weiß, dass meine Antwort auf diese Frage etwas spät ist, aber hier ist ein großartiger Artikel auf www.html5rocks.com - Tauchen Sie tief in das trübe Wasser des Ladens von Skripten ein .

In diesem Artikel wird der Schluss gezogen, dass in Bezug auf die Browserunterstützung der beste Weg zum dynamischen Laden von JavaScript-Dateien ohne Blockierung des Renderns von Inhalten der folgende Weg ist:

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

Ein paar Tricks und Minimierungen später sind es 362 Bytes

!function(e,t,r){function n(){for(;d[0]&&"loaded"==d[0][f];)c=d.shift(),c[o]=!i.parentNode.insertBefore(c,i)}for(var s,a,c,d=[],i=e.scripts[0],o="onreadystatechange",f="readyState";s=r.shift();)a=e.createElement(t),"async"in i?(a.async=!1,e.head.appendChild(a)):i[f]?(d.push(a),a[o]=n):e.write("<"+t+' src="'+s+'" defer></'+t+">"),a.src=s}(document,"script",[
  "//other-domain.com/1.js",
  "2.js"
])
asmmahmud
quelle
Ich benutze den obigen Ansatz, es funktioniert gut in Chrome & Firefox, aber es gibt Probleme im IE-Browser
Mr. Roshan
1

Etwas wie das...

<script>
     $(document).ready(function() {
          $('body').append('<script src="https://maps.googleapis.com/maps/api/js?key=KEY&libraries=places&callback=getCurrentPickupLocation" async defer><\/script>');
     });
</script>
James Arnold
quelle
1

Hier ein einfaches Beispiel für eine Funktion zum Laden von JS-Dateien. Relevante Punkte:

  • Sie benötigen jQuery nicht, daher können Sie dies zunächst verwenden, um auch die Datei jQuery.js zu laden
  • Es ist asynchron mit Rückruf
  • Es stellt sicher, dass es nur einmal geladen wird, da es ein Gehäuse mit der Aufzeichnung der geladenen URLs führt und somit die Nutzung des Netzwerks vermeidet
  • im gegensatz zu jQuery $.ajaxoder$.getScript Sie können Nonces verwenden, um Probleme mit CSP zu lösen unsafe-inline. Nutzen Sie einfach die Eigenschaftscript.nonce
var getScriptOnce = function() {

    var scriptArray = []; //array of urls (closure)

    //function to defer loading of script
    return function (url, callback){
        //the array doesn't have such url
        if (scriptArray.indexOf(url) === -1){

            var script=document.createElement('script');
            script.src=url;
            var head=document.getElementsByTagName('head')[0],
                done=false;

            script.onload=script.onreadystatechange = function(){
                if ( !done && (!this.readyState || this.readyState == 'loaded' || this.readyState == 'complete') ) {
                    done=true;
                    if (typeof callback === 'function') {
                        callback();
                    }
                    script.onload = script.onreadystatechange = null;
                    head.removeChild(script);

                    scriptArray.push(url);
                }
            };

            head.appendChild(script);
        }
    };
}();

Jetzt benutzt du es einfach von

getScriptOnce("url_of_your_JS_file.js");
João Pimentel Ferreira
quelle
1

Ein absurder Einzeiler für diejenigen, die denken, dass das Laden einer js-Bibliothek nicht mehr als eine Codezeile erfordern sollte: P.

await new Promise((resolve, reject) => {let js = document.createElement("script"); js.src="mylibrary.js"; js.onload=resolve; js.onerror=reject; document.body.appendChild(js)});

Wenn es sich bei dem zu importierenden Skript um ein Modul handelt, können Sie die import(...)Funktion natürlich verwenden .


quelle
1

Mit Promises können Sie es so vereinfachen. Ladefunktion:

  const loadCDN = src =>
    new Promise((resolve, reject) => {
      if (document.querySelector(`head > script[src="${src}"]`) !== null) return resolve()
      const script = document.createElement("script")
      script.src = src
      script.async = true
      document.head.appendChild(script)
      script.onload = resolve
      script.onerror = reject
    })

Verwendung (asynchron / warten):

await loadCDN("https://.../script.js")

Verwendung (Versprechen):

loadCDN("https://.../script.js").then(res => {}).catch(err => {})

HINWEIS: Es gab eine ähnliche Lösung, die jedoch nicht überprüft, ob das Skript bereits geladen ist, und das Skript jedes Mal lädt. Dieser überprüft die src-Eigenschaft.

Radulle
quelle
0

Alle wichtigen Javascript-Bibliotheken wie jscript, prototype und YUI unterstützen das Laden von Skriptdateien. In YUI können Sie beispielsweise nach dem Laden des Kerns die folgenden Schritte ausführen, um das Kalendersteuerelement zu laden

var loader = new YAHOO.util.YUILoader({

    require: ['calendar'], // what components?

    base: '../../build/',//where do they live?

    //filter: "DEBUG",  //use debug versions (or apply some
                        //some other filter?

    //loadOptional: true, //load all optional dependencies?

    //onSuccess is the function that YUI Loader
    //should call when all components are successfully loaded.
    onSuccess: function() {
        //Once the YUI Calendar Control and dependencies are on
        //the page, we'll verify that our target container is 
        //available in the DOM and then instantiate a default
        //calendar into it:
        YAHOO.util.Event.onAvailable("calendar_container", function() {
            var myCal = new YAHOO.widget.Calendar("mycal_id", "calendar_container");
            myCal.render();
        })
     },

    // should a failure occur, the onFailure function will be executed
    onFailure: function(o) {
        alert("error: " + YAHOO.lang.dump(o));
    }

 });

// Calculate the dependency and insert the required scripts and css resources
// into the document
loader.insert();
Darren Kopp
quelle
Sie können einen Blick auf den Link werfen: Antwort
Asmmahmud
0

Ich habe einige der oben genannten Beiträge mit einem funktionierenden Beispiel optimiert. Hier können wir auch css und js im selben Array angeben.

$(document).ready(function(){

if (Array.prototype.contains === undefined) {
Array.prototype.contains = function (obj) {
    var i = this.length;
    while (i--) { if (this[i] === obj) return true; }
    return false;
};
};

/* define object that will wrap our logic */
var jsScriptCssLoader = {

jsExpr : new RegExp( "js$", "i" ),
cssExpr : new RegExp( "css$", "i" ),
loadedFiles: [],

loadFile: function (cssJsFileArray) {
    var self = this;
    // remove duplicates with in array
    cssJsFileArray.filter((item,index)=>cssJsFileArray.indexOf(item)==index)
    var loadedFileArray = this.loadedFiles;
    $.each(cssJsFileArray, function( index, url ) {
            // if multiple arrays are loaded the check the uniqueness
            if (loadedFileArray.contains(url)) return;
            if( self.jsExpr.test( url ) ){
                $.get(url, function(data) {
                    self.addScript(data);
                });

            }else if( self.cssExpr.test( url ) ){
                $.get(url, function(data) {
                    self.addCss(data);
                });
            }

            self.loadedFiles.push(url);
    });

    // don't load twice accross different arrays

},
addScript: function (code) {
    var oNew = document.createElement("script");
    oNew.type = "text/javascript";
    oNew.textContent = code;
    document.getElementsByTagName("head")[0].appendChild(oNew);
},
addCss: function (code) {
    var oNew = document.createElement("style");
    oNew.textContent = code;
    document.getElementsByTagName("head")[0].appendChild(oNew);
}

};


//jsScriptCssLoader.loadFile(["css/1.css","css/2.css","css/3.css"]);
jsScriptCssLoader.loadFile(["js/common/1.js","js/2.js","js/common/file/fileReader.js"]);
});
VG P.
quelle
0

Für diejenigen unter Ihnen, die Einzeiler lieben:

import('./myscript.js');

Möglicherweise erhalten Sie einen Fehler wie:

Der Zugriff auf das Skript unter ' http: //..../myscript.js ' vom Ursprung ' http://127.0.0.1 ' wurde durch die CORS-Richtlinie blockiert: Es ist kein Header 'Access-Control-Allow-Origin' vorhanden die angeforderte Ressource.

In diesem Fall können Sie auf Folgendes zurückgreifen:

fetch('myscript.js').then(r => r.text()).then(t => new Function(t)());
Ludmil Tinkov
quelle