Dynamicznie ładuj plik JavaScript

167

Jak niezawodnie i dynamicznie ładować plik JavaScript? Można to wykorzystać do zaimplementowania modułu lub komponentu, który po „zainicjowaniu” będzie dynamicznie ładował wszystkie potrzebne skrypty biblioteki JavaScript na żądanie.

Klient korzystający z komponentu nie musi wczytywać wszystkich plików skryptów biblioteki (i ręcznie wstawiać <script>znaczniki na swojej stronie internetowej), które implementują ten komponent - tylko „główny” plik skryptu komponentu.

W jaki sposób główne biblioteki JavaScript osiągają to (Prototype, jQuery itp.)? Czy te narzędzia łączą wiele plików JavaScript w jedną przeznaczoną do redystrybucji wersję „kompilacji” pliku skryptu? A może wykonują jakieś dynamiczne ładowanie pomocniczych skryptów „bibliotecznych”?

Dodatek do tego pytania: czy istnieje sposób obsługi zdarzenia po załadowaniu dynamicznie dołączanego pliku JavaScript? Prototype służy document.observedo wydarzeń obejmujących cały dokument. Przykład:

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

Jakie są dostępne zdarzenia dla elementu skryptu?

Adam
źródło

Odpowiedzi:

84

Możesz pisać dynamiczne tagi skryptów (używając Prototype ):

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

Problem polega na tym, że nie wiemy, kiedy zewnętrzny plik skryptu jest w pełni załadowany.

Często chcemy, aby nasz zależny kod znajdował się w następnej linii i lubimy pisać coś takiego:

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

Istnieje sprytny sposób na wstrzyknięcie zależności skryptu bez potrzeby wywołania zwrotnego. Wystarczy pobrać skrypt za pośrednictwem synchronicznego żądania AJAX i ocenić skrypt na poziomie globalnym.

Jeśli używasz Prototype, metoda Script.load wygląda następująco:

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
źródło
Co mogę zrobić, aby działał w wielu domenach? (ładowanie skryptu z 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: Nie kontroluję web.archive.org.
user2284570
Następnie musisz zeskrobać dane, do których chcesz uzyskać dostęp za pomocą innego programu i dostarczyć je sobie
David Schumann,
co to jest Ajax.Request ??
bluejayke
72

W javascript nie ma importu / dołączania / wymagania, ale są dwa główne sposoby osiągnięcia tego, co chcesz:

1 - Możesz załadować go za pomocą wywołania AJAX, a następnie użyć eval.

Jest to najprostszy sposób, ale jest ograniczony do Twojej domeny ze względu na ustawienia bezpieczeństwa Javascript, a używanie eval otwiera drzwi do błędów i hacków.

2 - Dodaj tag script z adresem URL skryptu w kodzie HTML.

Zdecydowanie najlepszy sposób. Możesz załadować skrypt nawet z obcego serwera i jest czysty, gdy używasz parsera przeglądarki do oceny kodu. Możesz umieścić tag w nagłówku strony internetowej lub na dole jej treści.

Oba te rozwiązania są tutaj omówione i zilustrowane.

Teraz jest duży problem, o którym musisz wiedzieć. Oznacza to, że zdalnie ładujesz kod. Nowoczesne przeglądarki internetowe załadują plik i będą wykonywać bieżący skrypt, ponieważ ładują wszystko asynchronicznie, aby poprawić wydajność.

Oznacza to, że jeśli użyjesz tych sztuczek bezpośrednio, nie będziesz mógł użyć nowo załadowanego kodu w następnej linii po tym, jak poprosiłeś o załadowanie, ponieważ nadal będzie się ładował.

Np .: my_lovely_script.js zawiera 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

Następnie ponownie ładujesz stronę, naciskając klawisz F5. I to działa! Mylące...

Więc co z tym zrobić?

Cóż, możesz skorzystać z hackowania, który autor sugeruje w linku, który ci dałem. Podsumowując, dla osób w pośpiechu używa en event do uruchomienia funkcji zwrotnej, gdy skrypt jest ładowany. Możesz więc umieścić cały kod za pomocą zdalnej biblioteki w funkcji wywołania zwrotnego. EG:

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

Następnie piszesz kod, którego chcesz użyć PO załadowaniu skryptu w funkcji lambda:

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

Następnie uruchom to wszystko:

loadScript("my_lovely_script.js", myPrettyCode);

Ok, rozumiem. Ale pisanie tego wszystkiego jest trudne.

Cóż, w takim przypadku możesz jak zwykle skorzystać z fantastycznego darmowego frameworka jQuery, który pozwoli ci zrobić to samo w jednej linii:

$.getScript("my_lovely_script.js", function() {
    alert("Script loaded and executed.");
    // here you can use anything you defined in the loaded script
});
Dhaval Shah
źródło
11
nie mogę uwierzyć, jak niedoceniana jest ta odpowiedź. Dziękuję Ci.
naftalimich
2
Założę się, że dzieje się tak dlatego, że ludzie nie lubią czytać dalej niż pierwsza linijka, chyba że obiecuje im, że będą bogaci, jeśli „po prostu wykonają te trzy sekretne kroki do sukcesu”.
Costa
To działa. Mam nadzieję, że podzielenie się tutaj moim doświadczeniem pozwoli zaoszczędzić czas, ponieważ spędziłem nad tym wiele godzin. Używam Angulara 6 i zastosowanego szablonu (html, css, jquery) Problem polega na tym, że szablon ma plik js, który ładuje się po załadowaniu elementów html w celu dołączenia zdarzeń nasłuchujących. Ten plik js był trudny do wykonania po załadowaniu aplikacji kątowej. Dodanie go do tagu skryptów angular app (angular.json) scripts połączy je, ale nie spowoduje wykonania tego pliku js po załadowaniu. Jest to zbyt dużo kodu, aby można go było przepisać na maszynie, więc było to bardzo pomocne. Następny komentarz
Podam
Po prostu użyłem tego kodu w następujący sposób: ngAfterViewInit () {debugger; $ .getScript ("/ asset / js / jquery.app.js", function () {alert ("Skrypt załadowany i wykonany."); // tutaj możesz użyć wszystkiego, co zdefiniowałeś w załadowanym skrypcie}); }
Nour Lababidi
W przypadku
``
28

Użyłem ostatnio znacznie mniej skomplikowanej wersji z jQuery :

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

Działało świetnie w każdej przeglądarce, w której to testowałem: IE6 / 7, Firefox, Safari, Opera.

Aktualizacja: wersja bez jQuery:

<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
źródło
25
To świetnie ... chyba że próbujesz załadować jquery.
1
Wygląda na to, że jQuery pojawi się we wtyczce Requirements w jQuery Core w celu uzyskania przyszłej wersji: plugins.jquery.com/project/require
Adam
1
Jeszcze lepszym sposobem korzystania z jQuery jest użycie $.getScript. Zobacz moją odpowiedź.
Muhd
1
Modernizr (yepnope.js) lub lab.js to odpowiednie rozwiązanie. użycie ciężkiej biblioteki skryptów (która musi zostać załadowana jako pierwsza) nie jest najlepszym rozwiązaniem w przypadku urządzeń mobilnych lub wielu innych sytuacji.
1nfiniti
2
@MaulikGangani starsze przeglądarki i walidatory HTML zinterpretowałyby to jako token do zakończenia skryptu.
travis
20

Zrobiłem w zasadzie to samo, co ty, Adam, ale z niewielką modyfikacją, aby upewnić się, że dodałem tag do głowy, aby wykonać zadanie. Po prostu utworzyłem funkcję dołączania (kod poniżej) do obsługi zarówno plików skryptu, jak i plików css.

Ta funkcja sprawdza również, czy skrypt lub plik CSS nie został jeszcze załadowany dynamicznie. Nie sprawdza ręcznie zakodowanych wartości i być może istniał lepszy sposób na zrobienie tego, ale spełnił swój cel.

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 );
}
Blady koń
źródło
Obawiam się, że bez większego kontekstu niż ta marynata nie mam żadnych sugestii. Powyższy kod działa tak jak jest, został pobrany bezpośrednio z działającego projektu.
palehorse
5
@palehorse, Mike i Muhd, mają rację, to może działać w twoim projekcie, ale to dlatego, że zmienne „includedFile” i „Array” muszą być zdefiniowane w innym miejscu twojego projektu, ten kod sam w sobie nie będzie działać, może być lepiej go edytować, aby działał poza kontekstem projektu lub przynajmniej dodać komentarz wyjaśniający, jakie są te niezdefiniowane zmienne (typ itp.)
user280109
14

kolejna niesamowita odpowiedź

$.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
źródło
2
Co mogę zrobić, aby działał w wielu domenach? (ładowanie skryptu z http://web.archive.org/web/20140905044059/http://www.howtocreate.co.uk/operaStu‌​ff/userjs/aagmfunctions.js)
user2284570
9

Oto przykładowy kod, który znalazłem ... czy ktoś ma lepszy sposób?

  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
źródło
Co mogę zrobić, aby działał w wielu domenach? (ładowanie skryptu z http://web.archive.org/web/20140905044059/http://www.howtocreate.co.uk/operaStu‌​ff/userjs/aagmfunctions.js)
user2284570
6

Jeśli masz już załadowane jQuery, powinieneś użyć $ .getScript .

Ma to przewagę nad innymi odpowiedziami tutaj, ponieważ masz wbudowaną funkcję zwrotną (aby zagwarantować, że skrypt zostanie załadowany przed uruchomieniem zależnego kodu) i możesz kontrolować buforowanie.

Muhd
źródło
4

Jeśli chcesz załadować skrypt SYNC , musisz dodać tekst skryptu bezpośrednio do tagu HTML HEAD. Dodanie go jako wyzwoli obciążenie ASYNC . Aby synchronicznie załadować tekst skryptu z pliku zewnętrznego, użyj XHR. Poniżej krótka próbka (wykorzystuje części innych odpowiedzi w tym i innych postach):

/*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
źródło
3

czy ktoś ma lepszy sposób?

Myślę, że samo dodanie skryptu do treści byłoby łatwiejsze niż dodanie go do ostatniego węzła na stronie. Co powiesz na to:

function include(url) {
  var s = document.createElement("script");
  s.setAttribute("type", "text/javascript");
  s.setAttribute("src", url);
  document.body.appendChild(s);
}
Joseph Pecoraro
źródło
Co mogę zrobić, aby działał w wielu domenach? (ładowanie skryptu z http://web.archive.org/web/20140905044059/http://www.howtocreate.co.uk/operaStu‌​ff/userjs/aagmfunctions.js)
user2284570
3

użyłem jeszcze innego rozwiązania, które znalazłem w sieci ... to jest pod Creative Commons i sprawdza, czy źródło zostało uwzględnione przed wywołaniem funkcji ...

plik można znaleźć tutaj: 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
źródło
Co mogę zrobić, aby działał w wielu domenach? (ładowanie skryptu z http://web.archive.org/web/20140905044059/http://www.howtocreate.co.uk/operaStu‌​ff/userjs/aagmfunctions.js)
user2284570
3

Właśnie dowiedziałem się o świetnej funkcji w YUI 3 (w momencie pisania tego tekstu dostępna w wersji zapoznawczej). Możesz łatwo wstawiać zależności do bibliotek YUI i do "zewnętrznych" modułów (czego szukasz) bez zbytniego kodu: YUI Loader .

Odpowiada również na drugie pytanie dotyczące funkcji wywoływanej po załadowaniu modułu zewnętrznego.

Przykład:

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

U mnie zadziałało idealnie i ma tę zaletę, że zarządza zależnościami. Zapoznaj się z dokumentacją YUI, aby zobaczyć przykład z kalendarzem YUI 2 .

Kariem
źródło
Jest to prawdopodobnie idealne, z wyjątkiem tego, że YUI jest nieprzyzwoicie duży dla tej funkcji.
Muhd
3

Pojawił się nowy proponowany standard ECMA zwany importem dynamicznym , który został niedawno włączony do Chrome i Safari.

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

import(moduleSpecifier)
   .then(someModule => someModule.foo()); // executes foo method in someModule
Alister
źródło
2

Technika, której używamy w pracy, polega na żądaniu pliku javascript za pomocą żądania AJAX, a następnie eval () zwraca. Jeśli używasz biblioteki prototypów, obsługują one tę funkcjonalność w swoim wywołaniu Ajax.Request.

17 z 26
źródło
2

jquery rozwiązało to dla mnie dzięki funkcji .append () - użyło tego do załadowania całego pakietu jquery ui

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

Do użycia - w nagłówku twojego html / php / etc po zaimportowaniu jquery.js po prostu włączysz ten jeden plik, aby załadować całą bibliotekę, dołączając go do głowy ...

<script type="text/javascript" src="project.library.js"></script>
JM Design
źródło
2

Niech będzie ładny, krótki, prosty i łatwy w utrzymaniu! :]

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

Ten kod jest po prostu krótkim przykładem funkcjonalnym, który może wymagać dodatkowej funkcjonalności dla pełnego wsparcia na dowolnej (lub danej) platformie.

tfont
źródło
2

Dynamiczny import modułu wylądował w przeglądarce Firefox 67+ .

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

Z obsługą błędów:

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

Działa również dla wszelkiego rodzaju bibliotek niebędących modułami, w tym przypadku biblioteka jest dostępna w obiekcie okna w stary sposób, ale tylko na żądanie, co jest miłe.

Przykład użycia suncalc.js , serwer musi mieć włączony CORS, aby działał w ten sposób!

(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
źródło
1

Istnieją skrypty zaprojektowane specjalnie do tego celu.

yepnope.js jest wbudowany w Modernizr i lab.js jest bardziej zoptymalizowaną (ale mniej przyjazną dla użytkownika wersją.

Nie polecałbym tego robić za pomocą dużej biblioteki, takiej jak jquery lub prototyp - ponieważ jedną z głównych zalet programu ładującego skrypt jest możliwość wczesnego ładowania skryptów - nie powinieneś czekać, aż jquery i wszystkie elementy dom zostaną załadowane wcześniej uruchomienie sprawdzenia, czy chcesz dynamicznie załadować skrypt.

1nfiniti
źródło
1

Napisałem prosty moduł, który automatyzuje zadanie importowania / włączania skryptów modułu w JavaScript. Spróbuj i poświęć trochę opinii! :) Szczegółowe wyjaśnienie kodu można znaleźć w tym poście na blogu: 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
źródło
1

Zagubiłem się we wszystkich tych próbkach, ale dziś musiałem załadować zewnętrzny plik .js z mojego głównego .js i zrobiłem to:

document.write("<script src='https://www.google.com/recaptcha/api.js'></script>");
adrianTNT
źródło
możesz zerknąć na link: odpowiedź
asmmahmud
1

Oto prosty z oddzwanianiem i obsługą IE:

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

});
Jakub
źródło
1

Wiem, że moja odpowiedź jest nieco spóźniona na to pytanie, ale oto świetny artykuł na www.html5rocks.com - Głęboko zanurz się w mętnych wodach ładowania skryptów .

Z tego artykułu wynika, że ​​jeśli chodzi o obsługę przeglądarek, najlepszym sposobem na dynamiczne ładowanie pliku JavaScript bez blokowania renderowania treści jest następujący sposób:

Biorąc pod uwagę, że masz cztery nazwane skrypty script1.js, script2.js, script3.js, script4.js, możesz to zrobić, stosując 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);
});

Teraz Spec mówi : Pobierz razem, wykonuj w kolejności, gdy tylko wszystko zostanie pobrane.

Firefox <3.6, Opera mówi: Nie mam pojęcia, co to za „asynchronizacja”, ale tak się składa, że ​​wykonuję skrypty dodane przez JS w kolejności, w jakiej są dodawane.

Safari 5.0 mówi: Rozumiem „async”, ale nie rozumiem ustawiania go na „false” w JS. Wykonam twoje skrypty, gdy tylko wylądują, w dowolnej kolejności.

IE <10 mówi: Nie mam pojęcia o „async”, ale można obejść ten problem przy użyciu „onreadystatechange”.

Wszystko inne mówi: jestem twoim przyjacielem, zrobimy to na podstawie książki.

Teraz pełny kod z obejściem IE <10:

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

Kilka sztuczek i minifikacja później, to 362 bajty

!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
źródło
Używam powyższego podejścia, działa dobrze w Chrome i Firefox, ale napotykam problemy w przeglądarce IE
Mr. Roshan
1

Coś takiego...

<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
źródło
1

Oto prosty przykład funkcji ładującej pliki JS. Istotne punkty:

  • nie potrzebujesz jQuery, więc możesz użyć tego początkowo do załadowania również pliku jQuery.js
  • jest asynchroniczny z wywołaniem zwrotnym
  • zapewnia, że ​​ładuje się tylko raz, ponieważ utrzymuje obudowę z zapisem załadowanych adresów URL, unikając w ten sposób korzystania z sieci
  • w przeciwieństwie do jQuery $.ajaxlub $.getScriptmożesz użyć wartości nonce, rozwiązując w ten sposób problemy z CSP unsafe-inline. Wystarczy skorzystać z nieruchomościscript.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);
        }
    };
}();

Teraz używasz go po prostu przez

getScriptOnce("url_of_your_JS_file.js");
João Pimentel Ferreira
źródło
1

Absurdalna jedna linijka, dla tych, którzy uważają, że ładowanie biblioteki js nie powinno zająć więcej niż jednej linii kodu: 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)});

Oczywiście, jeśli skrypt, który chcesz zaimportować, jest modułem, możesz użyć import(...)funkcji.


źródło
1

Dzięki Obietnicom możesz to uprościć w ten sposób. Funkcja ładowarki:

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

Użycie (async / await):

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

Użycie (obietnica):

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

UWAGA: było jedno podobne rozwiązanie, ale nie sprawdza, czy skrypt jest już załadowany i ładuje skrypt za każdym razem. Ten sprawdza właściwość src.

korzeń
źródło
0

wszystkie główne biblioteki javascript, takie jak jscript, prototype, YUI obsługują ładowanie plików skryptów. Na przykład w YUI, po załadowaniu rdzenia, możesz wykonać następujące czynności, aby załadować formant kalendarza

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
źródło
możesz zerknąć na link: odpowiedź
asmmahmud
0

Poprawiłem niektóre z powyższych postów za pomocą przykładu roboczego. Tutaj możemy również podać css i js w tej samej tablicy.

$(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
źródło
0

Dla tych z Was, którzy kochają jednolinijki:

import('./myscript.js');

Istnieje prawdopodobieństwo, że pojawi się błąd, taki jak:

Dostęp do skryptu pod adresemhttp: //..../myscript.js ” ze źródła „ http://127.0.0.1 ” został zablokowany przez zasady CORS: w witrynie nie ma nagłówka „Access-Control-Allow-Origin” żądany zasób.

W takim przypadku możesz wrócić do:

fetch('myscript.js').then(r => r.text()).then(t => new Function(t)());
Ludmil Tinkov
źródło