Jak dołączyć plik JavaScript do innego pliku JavaScript?


5193

Czy w JavaScript jest coś podobnego do @importCSS, co pozwala na dołączenie pliku JavaScript do innego pliku JavaScript?



82
@Daniel, nie chcę używać połączenia AJAX.
Alec Smart

13
Dlaczego nie zadeklarować zaimportowanego pliku przed drugim, który go wymaga, po prostu używając uporządkowanych scripttagów?
falsarella,

6
@Claudiu To nic nie pomogłoby w zaimportowaniu , ale powinno również działać. Jeśli masz plik JS, który zależy od innego pliku JS, najpierw zadeklaruj znaczniki skryptów plików zależności, aby później załadować już jego zależności. Jeśli masz sytuację, w której nie jest to możliwe, odpowiedzi tutaj powinny być pomocne.
falsarella,

1
jaka jest praktyczna zaleta robienia tego? tak czy inaczej podstawa kodu zależna od pliku javascript nie zostanie załadowana i nie zacznie działać w żadnym wypadku nie zostanie załadowana!
Ciasto piekarz

Odpowiedzi:


4470

Stare wersje JavaScript nie importowały, nie zawierały ani nie wymagały, dlatego opracowano wiele różnych podejść do tego problemu.

Ale od 2015 r. (ES6) JavaScript ma standard modułów ES6 do importowania modułów w Node.js, który jest również obsługiwany przez większość nowoczesnych przeglądarek .

W celu zapewnienia zgodności ze starszymi przeglądarkami można używać narzędzi do budowania, takich jak Webpack i Rollup, i / lub narzędzi do transpozycji, takich jak Babel .

Moduły ES6

Moduły ECMAScript (ES6) są obsługiwane w Node.js od wersji 8.5, z --experimental-modulesflagą, i od co najmniej Node.js wersji 13.8.0 bez flagi. Aby włączyć „ESM” (w porównaniu z poprzednim systemie modułu CommonJS stylu node.js za [ „CJS”]) użyć albo "type": "module"w package.jsonlub dać pliki z rozszerzeniem .mjs. (Podobnie moduły napisane przy użyciu poprzedniego modułu CJS Node.js można nazwać, .cjsjeśli domyślnym jest ESM.)

Używanie package.json:

{
    "type": "module"
}

Następnie module.js:

export function hello() {
  return "Hello";
}

Następnie main.js:

import { hello } from './module.js';
let val = hello();  // val is "Hello";

Używając .mjsbędziesz miał module.mjs:

export function hello() {
  return "Hello";
}

Następnie main.mjs:

import { hello } from './module.mjs';
let val = hello();  // val is "Hello";

Moduły ECMAScript w przeglądarkach

Przeglądarki mają obsługę bezpośredniego ładowania modułów ECMAScript (nie są wymagane żadne narzędzia, takie jak Webpack) od Safari 10.1, Chrome 61, Firefox 60 i Edge 16. Sprawdź bieżącą obsługę w caniuse . Nie ma potrzeby używania .mjsrozszerzenia Node.js ; przeglądarki całkowicie ignorują rozszerzenia plików w modułach / skryptach.

<script type="module">
  import { hello } from './hello.mjs'; // Or it could be simply `hello.js`
  hello('world');
</script>
// hello.mjs -- or it could be simply `hello.js`
export function hello(text) {
  const div = document.createElement('div');
  div.textContent = `Hello ${text}`;
  document.body.appendChild(div);
}

Czytaj więcej na https://jakearchibald.com/2017/es-modules-in-browsers/

Dynamiczne importowanie w przeglądarkach

Dynamiczne importowanie pozwala skryptowi ładować inne skrypty w razie potrzeby:

<script type="module">
  import('hello.mjs').then(module => {
      module.hello('world');
    });
</script>

Czytaj więcej na https://developers.google.com/web/updates/2017/11/dynamic-import

Wymagane są pliki Node.js

Starszym stylem modułu CJS, wciąż szeroko stosowanym w Node.js, jest module.exports/require system.

// mymodule.js
module.exports = {
   hello: function() {
      return "Hello";
   }
}
// server.js
const myModule = require('./mymodule');
let val = myModule.hello(); // val is "Hello"   

Istnieją inne sposoby włączania przez JavaScript zewnętrznych treści JavaScript w przeglądarkach, które nie wymagają wstępnego przetwarzania.

Ładowanie AJAX

Możesz załadować dodatkowy skrypt z wywołaniem AJAX, a następnie użyć go evaldo uruchomienia. Jest to najprostszy sposób, ale jest ograniczony do Twojej domeny ze względu na model bezpieczeństwa piaskownicy JavaScript. Korzystanie evalrównież otwiera drzwi do błędów, włamań i problemów bezpieczeństwa.

Pobierz ładowanie

Podobnie jak w przypadku dynamicznych importów, możesz załadować jeden lub wiele skryptów z fetchwywołaniem, używając obietnic do kontrolowania kolejności wykonywania zależności skryptów za pomocą biblioteki Fetch Inject :

fetchInject([
  'https://cdn.jsdelivr.net/momentjs/2.17.1/moment.min.js'
]).then(() => {
  console.log(`Finish in less than ${moment().endOf('year').fromNow(true)}`)
})

Ładowanie jQuery

JQuery biblioteki zapewnia funkcjonalność ładowania w jednej linii :

$.getScript("my_lovely_script.js", function() {
   alert("Script loaded but not necessarily executed.");
});

Dynamiczne ładowanie skryptu

Możesz dodać tag HTML ze skryptu URL do HTML. Jest to idealne rozwiązanie, aby uniknąć narzutów związanych z jQuery.

Skrypt może nawet znajdować się na innym serwerze. Ponadto przeglądarka ocenia kod. <script>Znacznik może być wstrzykiwany do każdej strony internetowej <head>, lub włożona tuż przed zamykającym </body>tagiem.

Oto przykład, jak to może działać:

function dynamicallyLoadScript(url) {
    var script = document.createElement("script");  // create a script DOM node
    script.src = url;  // set its src to the provided URL

    document.head.appendChild(script);  // add it to the end of the head section of the page (could change 'head' to 'body' to add it to the end of the body section instead)
}

Ta funkcja doda nowy <script>znacznik na końcu sekcji nagłówka strony, gdzie srcatrybut jest ustawiony na adres URL podany funkcji jako pierwszy parametr.

Oba te rozwiązania zostały omówione i zilustrowane w JavaScript Madness: Dynamic Script Loading .

Wykrywanie, kiedy skrypt został wykonany

Teraz jest duży problem, o którym musisz wiedzieć. Oznacza to, że zdalnie ładujesz kod . Nowoczesne przeglądarki internetowe załadują plik i wykonają bieżący skrypt, ponieważ ładują wszystko asynchronicznie, aby poprawić wydajność. (Dotyczy to zarówno metody jQuery, jak i metody ręcznego dynamicznego ładowania skryptów.)

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

Na przykład: my_lovely_script.jszawiera 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 uderzanie strony F5. I to działa! Zagmatwane...

Co z tym zrobić?

Możesz użyć hacka, który autor sugeruje w linku, który ci podałem. Podsumowując, dla osób, którym się spieszy, używa zdarzenia do uruchomienia funkcji zwrotnej po załadowaniu skryptu. Możesz więc umieścić cały kod za pomocą biblioteki zdalnej w funkcji wywołania zwrotnego. Na przykład:

function loadScript(url, callback)
{
    // Adding the script tag to the head as suggested before
    var head = document.head;
    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 do funkcji lambda :

var myPrettyCode = function() {
   // Here, do whatever you want
};

Następnie wykonujesz to wszystko:

loadScript("my_lovely_script.js", myPrettyCode);

Zauważ, że skrypt może zostać wykonany po załadowaniu DOM lub wcześniej, w zależności od przeglądarki i tego, czy podałeś wiersz script.async = false;. Ogólnie jest świetny artykuł na temat ładowania Javascript .

Scalanie / przetwarzanie wstępne kodu źródłowego

Jak wspomniano na początku tej odpowiedzi, wielu programistów używa w swoich projektach narzędzi do kompilacji / transpilacji, takich jak Parcel, Webpack lub Babel, co pozwala im korzystać z nadchodzącej składni JavaScript, zapewniać zgodność wsteczną dla starszych przeglądarek, łączyć pliki, minimalizować, wykonać dzielenie kodu itp.


130
Nie, ale ktoś, kto używa czegoś tak zaawansowanego jak Rhino, inaczej nie zadałby tego pytania.
e-satis,

192
Aby być kompletnym, istnieje trzeci sposób: w niektórych rozwiązaniach, gdy kontrolujesz oba pliki javascript, możesz po prostu utworzyć 1 gigantyczny plik javascript, który łączy zawartość obu plików.
Ropucha

20
Nie powinien „document.createElement („ my_lovely_script.js ”);” w przykładzie może być „document.createElement („ script ”)”?
Russell Silva,

14
W jaki sposób eval otwiera drzwi do włamań, jeśli wykonujesz swój kod?
Vince Panuccio

6
Twoja odpowiedź wymaga pewnych modyfikacji dla IE ( onreadystatechangezdarzenia i readyStatewłaściwości). Ponadto dynamiczne ładowanie skryptów nie korzysta ze skanerów wstępnego ładowania przeglądarki. Poleć ten artykuł HTML5Rocks: html5rocks.com/en/tutorials/speed/script-loading
ruhong

570

Jeśli ktoś szuka czegoś bardziej zaawansowanego, wypróbuj RequireJS . Dostaniesz dodatkowe korzyści, takie jak zarządzanie zależnościami, lepsza współbieżność i unikanie powielania (czyli pobierania skryptu więcej niż jeden raz).

Możesz pisać pliki JavaScript w „modułach”, a następnie określać je jako zależności w innych skryptach. Lub możesz użyć RequireJS jako prostego rozwiązania „idź dostać ten skrypt”.

Przykład:

Zdefiniuj zależności jako moduły:

some-dependency.js

define(['lib/dependency1', 'lib/dependency2'], function (d1, d2) {

     //Your actual script goes here.   
     //The dependent scripts will be fetched if necessary.

     return libraryObject;  //For example, jQuery object
});

Implementacja.js to „główny” plik JavaScript, który zależy od pliku dependence.js

require(['some-dependency'], function(dependency) {

    //Your script goes here
    //some-dependency.js is fetched.   
    //Then your script is executed
});

Fragment GITHub README:

RequireJS ładuje zwykłe pliki JavaScript, a także bardziej zdefiniowane moduły. Jest zoptymalizowany do użytku w przeglądarce, w tym w Web Worker, ale może być używany w innych środowiskach JavaScript, takich jak Rhino i Node. Implementuje interfejs API modułu asynchronicznego.

RequireJS używa prostych tagów skryptowych do ładowania modułów / plików, więc powinno to umożliwić łatwe debugowanie. Można go użyć do załadowania istniejących plików JavaScript, dzięki czemu można dodać go do istniejącego projektu bez konieczności ponownego zapisywania plików JavaScript.

...


1
@aaaidan: Powód MattDmo plus opiera się na zewnętrznej bibliotece, która w zamian opiera się na zaakceptowanej odpowiedzi.
David Mulder

1
Aby przezwyciężyć wymagany.js, najnowszym byłby kątowy js, który jest bardziej stabilny i łatwy w użyciu wraz z innymi wiążącymi i bogatymi funkcjami HTML.
zeeshan,

5
-1: Te abstrakcje - „some_dependency” - są naprawdę słabe, a indeksy powodują zamieszanie. Z trudem rozumiem, jak wygląda przykład działającego kodu. Gdyby autor podał przykład roboczy, prawie każdy byłby w stanie dostosować go i uogólnić do swoich potrzeb.
Tegiri Nenashi,

1
nie działa dobrze z MVC i pakietowaniem skryptów z automatyczną minimalizacją
Triynko

1
„dodaj .. bez konieczności ponownego zapisywania plików JavaScript”; fajnie, ale jak dokładnie? Kundel. odpowiedź sugeruje dodanie takiego „wymaganego” (nie „wymaganego”?) do głównego skryptu + takiego „zdefiniowania” do każdego zależnego pliku, tak? Ale to JEST przepisane, więc co nie zostało przepisane? -reszt kodu, ponieważ RequireJS pozwala plikowi ea na tworzenie globalnych definicji jak zwykle? -czy to? Właśnie próbowałem tego, + (zapomniałeś?) <Script src = " requjs.org/docs/release/2.2.0/comments/require.js "> </script>, a następnie „Requjs ([' GoogleDrive.com/host /..',..],function(a,b) {mycode}) ': Firebug mówi, że każda osoba zależna jest załadowana, ALE jej defekty nie są zdefiniowane w mycode i później.
Destiny Architect

195

Tam faktycznie jest to sposób, aby załadować plik JavaScript nie asynchronicznie, więc można korzystać z funkcji zawartych w nowo załadowanej właściwy plik po załadowaniu go i myślę, że to działa we wszystkich przeglądarkach.

Trzeba użyć jQuery.append()na <head>elemencie strony, to jest:

$("head").append('<script type="text/javascript" src="' + script + '"></script>');

Jednak ta metoda ma również problem: jeśli wystąpi błąd w importowanym pliku JavaScript, Firebug (a także konsola błędów Firefox i narzędzia programistyczne Chrome ) niepoprawnie zgłosi swoje miejsce, co jest dużym problemem, jeśli używasz Firebug do śledzenia Błędy JavaScript w dół dużo (robię). Firebug po prostu nie wie o nowo załadowanym pliku z jakiegoś powodu, więc jeśli wystąpi błąd w tym pliku, zgłasza, że ​​wystąpił w głównym pliku HTML , i będziesz miał problemy ze znalezieniem prawdziwej przyczyny błędu.

Ale jeśli nie jest to dla ciebie problemem, ta metoda powinna działać.

Napisałem wtyczkę jQuery o nazwie $ .import_js (), która używa tej metody:

(function($)
{
    /*
     * $.import_js() helper (for JavaScript importing within JavaScript code).
     */
    var import_js_imported = [];

    $.extend(true,
    {
        import_js : function(script)
        {
            var found = false;
            for (var i = 0; i < import_js_imported.length; i++)
                if (import_js_imported[i] == script) {
                    found = true;
                    break;
                }

            if (found == false) {
                $("head").append('<script type="text/javascript" src="' + script + '"></script>');
                import_js_imported.push(script);
            }
        }
    });

})(jQuery);

Wszystko, co musisz zrobić, aby zaimportować JavaScript, to:

$.import_js('/path_to_project/scripts/somefunctions.js');

Zrobiłem też prosty test w tym przykładzie .

Zawiera main.jsplik w głównym HTML, a następnie skrypt main.jsużywa $.import_js()do importowania dodatkowego pliku o nazwie included.js, który definiuje tę funkcję:

function hello()
{
    alert("Hello world!");
}

I zaraz po tym included.js, hello()funkcja jest wywoływana, a otrzymasz powiadomienie.

(Ta odpowiedź jest odpowiedzią na komentarz e-satis).


Próbuję tej metody, ale dla mnie nie działa, element po prostu nie pojawia się w tagu head.
strony

16
@juanpastas - użyj jQuery.getScript, dzięki czemu nie musisz się martwić pisaniem wtyczki ...
MattDMo

6
Hmm, zgodnie z tym artykułem , dodanie scriptelementu headspowoduje, że będzie on działał asynchronicznie, chyba że asyncjest to specjalnie ustawione false.
Flimm,

1
Czy zmienna skryptowa nie powinna mieć zakodowanych encji html? Jeśli link zawiera ", kod się
zepsuje

1
@Flimm, drogi panie, ja i mój zespół dziękujemy za komentarz i osobiście jestem ci winien piwo, nawet jeśli kiedykolwiek spotkamy się osobiście
Alex

155

Innym sposobem, który moim zdaniem jest znacznie czystszy, jest utworzenie synchronicznego żądania Ajax zamiast użycia <script>tagu. Tak też obejmuje Node.js.

Oto przykład użycia jQuery:

function require(script) {
    $.ajax({
        url: script,
        dataType: "script",
        async: false,           // <-- This is the key
        success: function () {
            // all good...
        },
        error: function () {
            throw new Error("Could not load script " + script);
        }
    });
}

Następnie możesz użyć go w kodzie, ponieważ zwykle używasz dołączenia:

require("/scripts/subscript.js");

I być w stanie wywołać funkcję z wymaganego skryptu w następnym wierszu:

subscript.doSomethingCool(); 

1
Dobre rozwiązanie, head include jest asynchroniczny, niestety, rozwiązanie ajax działa.
Matteo Conta,

17
Jak ktoś wspomniał, Requjs.org robi to i ma również prekompilator, który łączy pliki js razem, aby ładowały się szybciej. Możesz to sprawdzić.
Ariel

2
Znaleziono, że mogę to zrobić, dodając tę ​​dyrektywę na dole pliku dla Chrome: // @ sourceURL = view_index.js
Todd Vance

11
niestety, async: false jest teraz przestarzałe w jQuery. W przyszłości może się załamać, więc bym tego unikał.
sqram

3
@katsh Nie używamy tutaj obiektów jqXHR. Twój cytat nie wydaje się być kopią poprzedniego komentarza stwierdzającego, że async: falsepodobno jest przestarzały. Nie jest! Jak podaje cytat, tylko rzeczy związane z jqXHR są.
Zero3

101

To dla ciebie dobra wiadomość. Już wkrótce będziesz mógł łatwo załadować kod JavaScript. Stanie się standardowym sposobem importowania modułów kodu JavaScript i będzie częścią samego jądra JavaScript.

Musisz po prostu napisać, import cond from 'cond.js';aby załadować makro o nazwie condz pliku cond.js.

Nie musisz więc polegać na żadnej strukturze JavaScript ani jawnie wykonywać połączeń Ajax .

Odnosić się do:


12
wymaganie / importowanie do pliku js było już za długo. (IMO).
rwheadon

6
@ rwheadon tak wydaje się przerażające, że to nie jest część języka! Jak js ludzie robią cokolwiek, jest poza mną! Jestem nowy i wydaje się to najgorszym (z wielu)
kawałkiem

@ jonny-leeds Nawet bez wbudowanego ładowania modułu JavaScript w przeglądarce jest na tyle elastyczny, że możemy zaimplementować bibliotekę taką jak RequireJS do zarządzania modułami.
Gorliwy

8
połowa 2015 r. - Wciąż nie zaimplementowane w żadnej przeglądarce, developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
scape

tak, masz rację. teraz zaczynam czuć się źle z tego powodu :(. Po wdrożeniu we wszystkich przeglądarkach będzie to świetna funkcja wbudowana w javascript.
Imdad

96

Możliwe jest dynamiczne generowanie znacznika JavaScript i dołączanie go do dokumentu HTML z innego kodu JavaScript. Spowoduje to załadowanie ukierunkowanego pliku JavaScript.

function includeJs(jsFilePath) {
    var js = document.createElement("script");

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

    document.body.appendChild(js);
}

includeJs("/path/to/some/file.js");

8
@ e-satis - W rzeczywistości jest to zaleta, skrypt blokujący byłby blokowany. Konie na kursy, ale 9 razy na 10 chcesz opcję nieblokującą.
annakata

@ Svitlana - elementy skryptu utworzone w ten sposób są asynchroniczne. Obecnie można to postrzegać jako wykorzystanie luki, więc może to nie być dowód na przyszłość, nie widziałem niczego w żadnym standardzie, który to wyjaśnia.
annakata

Właściwie to cofam, zauważyłem, że przyłączasz się do ciała, a nie do głowy.
annakata

@annakata: Jaka jest różnica? BTW, yahoo sugeruje dodanie tagów skryptu na końcu treści, więc nic nie powinno się z tym nie zgadzać przy dynamicznym dodawaniu.
Svitlana Maksymchuk

7
@ asynchroniczny e-satis jest dobry, ponieważ nie zamraża twojej strony. Skorzystaj z oddzwaniania, aby otrzymać powiadomienie, gdy zostanie to wykonanejs.onload = callback;
Vitim.us

74

Oświadczenie importznajduje się w ECMAScript 6.

Składnia

import name from "module-name";
import { member } from "module-name";
import { member as alias } from "module-name";
import { member1 , member2 } from "module-name";
import { member1 , member2 as alias2 , [...] } from "module-name";
import name , { member [ , [...] ] } from "module-name";
import "module-name" as name;

... ale jak dotąd nie jest obsługiwany przez żadną przeglądarkę, zgodnie z tabelą kompatybilności na stronie, do której linkujesz.
Zero3

6
Możesz teraz napisać kod ES6 i skompilować go z Babel.js ( babeljs.io ) na dowolnym preferowanym obecnie systemie modułów (CommonJS / AMD / UMD): babeljs.io/docs/usage/modules
Jeremy Harris

@ Zero3 Najwyraźniej nowa IE (Edge) jest jedyna
Julian Avar

w 2019 roku IE nadal nie obsługuje tego w żadnej formie. Dlaczego Microsoft musi być tak sprzeczny z użytecznością?
GreySage

61

Może możesz użyć tej funkcji, którą znalazłem na tej stronie Jak dołączyć plik JavaScript do pliku JavaScript? :

function include(filename)
{
    var head = document.getElementsByTagName('head')[0];

    var script = document.createElement('script');
    script.src = filename;
    script.type = 'text/javascript';

    head.appendChild(script)
}

5
Przydaje się dodaćscript.onload = callback;
Vitim.us

@SvitlanaMaksymchuk, więc jeśli nie użyję, varzmienna będzie globalna?
Francisco Corrales Morales

@FranciscoCorrales tak.
Christopher Chiche

Kończy się globalnie z lub bez var :)
Vedran Maricevic.


54

Oto wersja synchroniczna bez jQuery :

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

Pamiętaj, że aby uzyskać działającą między domenami, serwer będzie musiał ustawić allow-originnagłówek w swojej odpowiedzi.


Doskonała funkcja! Ładuje JavaScript przed zapisaniem dodatkowego JS po treści. Bardzo ważne przy ładowaniu wielu skryptów.
tfont

3
@heinob: Co mogę zrobić, aby działał w wielu domenach? (ładowanie skryptu z http://web.archive.org/web/20140905044059/http://www.howtocreate.co.uk/operaStuff/userjs/aagmfunctions.js)
user2284570

@ user2284570: Jeśli jesteś właścicielem obcej domeny: ustaw nagłówek „allow-origin” w odpowiedzi serwera. Jeśli nie jesteś właścicielem: nic. Przepraszam! To jest polityka różnych źródeł.
heinob

2
@ user2284570: Rozumiem twój komentarz w ten sposób, że nie jesteś właścicielem domeny, z której chcesz załadować skrypt. W takim przypadku możesz załadować skrypt tylko za pomocą wstawionego <script>znacznika, a nie za pośrednictwem XMLHttpRequest.
heinob

2
Dla tych, którzy planują używać tego w Firefoksie (powiedzmy w przypadku skryptów imacros), dodaj ten wiersz na górze pliku:const XMLHttpRequest = Components.Constructor("@mozilla.org/xmlextras/xmlhttprequest;1");
Kwestion

49

Właśnie napisałem ten kod JavaScript (używając Prototypu do manipulacji DOM ):

var require = (function() {
    var _required = {};
    return (function(url, callback) {
        if (typeof url == 'object') {
            // We've (hopefully) got an array: time to chain!
            if (url.length > 1) {
                // Load the nth file as soon as everything up to the
                // n-1th one is done.
                require(url.slice(0, url.length - 1), function() {
                    require(url[url.length - 1], callback);
                });
            } else if (url.length == 1) {
                require(url[0], callback);
            }
            return;
        }
        if (typeof _required[url] == 'undefined') {
            // Haven't loaded this URL yet; gogogo!
            _required[url] = [];

            var script = new Element('script', {
                src: url,
                type: 'text/javascript'
            });
            script.observe('load', function() {
                console.log("script " + url + " loaded.");
                _required[url].each(function(cb) {
                    cb.call(); // TODO: does this execute in the right context?
                });
                _required[url] = true;
            });

            $$('head')[0].insert(script);
        } else if (typeof _required[url] == 'boolean') {
            // We already loaded the thing, so go ahead.
            if (callback) {
                callback.call();
            }
            return;
        }

        if (callback) {
            _required[url].push(callback);
        }
    });
})();

Stosowanie:

<script src="prototype.js"></script>
<script src="require.js"></script>
<script>
    require(['foo.js','bar.js'], function () {
        /* Use foo.js and bar.js here */
    });
</script>

Gist: http://gist.github.com/284442 .


7
jrburke napisał to jako RequireJS. Github: requjs.org/docs/requirements.html
Mike Caron

Czy nie powoduje to umieszczenia załadowanego skryptu poza zakresem, w którym wywoływana jest funkcja replace ()? Wydaje się, że eval () to jedyny sposób, aby to zrobić w zakresie. Czy jest inny sposób?
trusktr

44

Oto uogólniona wersja tego, jak Facebook robi to z ich wszechobecnym przyciskiem Like:

<script>
  var firstScript = document.getElementsByTagName('script')[0],
      js = document.createElement('script');
  js.src = 'https://cdnjs.cloudflare.com/ajax/libs/Snowstorm/20131208/snowstorm-min.js';
  js.onload = function () {
    // do stuff with your dynamically loaded script
    snowStorm.snowColor = '#99ccff';
  };
  firstScript.parentNode.insertBefore(js, firstScript);
</script>

Jeśli działa na Facebooku, będzie działał dla Ciebie.

Powodem, dla którego szukamy pierwszego scriptelementu zamiast headlub bodyjest to, że niektóre przeglądarki nie tworzą go, jeśli go brakuje, ale na pewno mamy scriptelement - ten. Przeczytaj więcej na http://www.jspatterns.com/the-ridiculous-case-of-adding-a-script-element/ .


3
Cholernie miło! Niektóre metody tutaj też działają, ale przy ustawieniu dynamicznym działa to najlepiej.
lipca16

Rozszerzyłem twój skrypt, aby wyeliminować dublaty
Kamil Dąbrowski

39

Jeśli chcesz w czystym JavaScript, możesz użyć document.write.

document.write('<script src="myscript.js" type="text/javascript"></script>');

Jeśli korzystasz z biblioteki jQuery, możesz użyć tej $.getScriptmetody.

$.getScript("another_script.js");

8
Czy Document.write nie usunąłby wszystkiego innego?
Eisa Adil

30

Możesz także składać skrypty za pomocą PHP :

Plik main.js.php:

<?php
    header('Content-type:text/javascript; charset=utf-8');
    include_once("foo.js.php");
    include_once("bar.js.php");
?>

// Main JavaScript code goes here

16
Wygląda na to, że chodzi o to, aby utrzymać to wszystko w javascript w interfejsie
Ariel

1
Dzięki za przypomnienie. Możesz również napisać w nagłówku HTML tagi <script>, aby załadować potrzebne pliki js (i tylko te).
Rolf,

29

Większość przedstawionych tu rozwiązań zakłada obciążenie dynamiczne. Zamiast tego szukałem kompilatora, który składałby wszystkie zależne pliki w jeden plik wyjściowy. Tak samo jak preprocesory Less / Sass zajmują się CSS@import zasadą . Ponieważ nie znalazłem nic przyzwoitego tego rodzaju, napisałem proste narzędzie do rozwiązania tego problemu.

Oto kompilator https://github.com/dsheiko/jsic , który bezpiecznie zastępuje $import("file-path")żądaną zawartość pliku. Oto odpowiednia wtyczka Grunt : https://github.com/dsheiko/grunt-jsic .

W gałęzi master jQuery łączą po prostu atomowe pliki źródłowe w jeden, zaczynając od intro.jsi kończąc na outtro.js. Nie odpowiada mi to, ponieważ nie zapewnia elastyczności w projektowaniu kodu źródłowego. Sprawdź, jak to działa z jsic:

src / main.js

var foo = $import("./Form/Input/Tel");

src / Form / Input / Tel.js

function() {
    return {
          prop: "",
          method: function(){}
    }
}

Teraz możemy uruchomić kompilator:

node jsic.js src/main.js build/mail.js

I pobierz połączony plik

build / main.js

var foo = function() {
    return {
          prop: "",
          method: function(){}
    }
};

2
Od tego posta wymyśliłem znacznie lepsze rozwiązanie - kompilator modułów CommonJS - github.com/dsheiko/cjsc Więc możesz po prostu pisać moduły CommonJs lub NodeJs i uzyskiwać do nich dostęp, utrzymując je w odizolowanych zakresach. Korzyści: Nie ma potrzeby wielu żądań HTTP, które wpływają na wydajność Nie trzeba ręcznie owijać kodu modułu - to jest odpowiedzialność kompilatora (więc lepsza czytelność kodu źródłowego) Nie potrzebujesz żadnych zewnętrznych bibliotek Jest kompatybilny z UMD- i moduły NodeJs (np. możesz adresować jQuery, Backbone jako moduły bez dotykania ich kodu)
Dmitry Sheiko

26

Jeśli zamierzasz załadować plik JavaScript, korzystasz z funkcji z importowanego / dołączonego pliku , możesz również zdefiniować obiekt globalny i ustawić funkcje jako elementy obiektu. Na przykład:

global.js

A = {};

plik1.js

A.func1 = function() {
  console.log("func1");
}

plik2.js

A.func2 = function() {
  console.log("func2");
}

main.js

A.func1();
A.func2();

Musisz tylko zachować ostrożność, dołączając skrypty do pliku HTML. Kolejność powinna wyglądać jak poniżej:

<head>
  <script type="text/javascript" src="global.js"></script>
  <script type="text/javascript" src="file1.js"></script>
  <script type="text/javascript" src="file2.js"></script>
  <script type="text/javascript" src="main.js"></script>
</head>

22

To powinno zrobić:

xhr = new XMLHttpRequest();
xhr.open("GET", "/soap/ajax/11.0/connection.js", false);
xhr.send();
eval(xhr.responseText);

5
evalto co się dzieje z nim. Od Crockfordaevaljest zła. Na evalfunkcja jest najbardziej nadużywane cechą JavaScript. Uniknąć. evalMa aliasów. Nie należy używać Functionkonstruktora. Nie przechodzą ciągi setTimeoutlub setInterval”. Jeśli nie przeczytałeś jego „JavaScript: The Good Parts”, wyjdź i zrób to teraz. Państwo będzie nie pożałujesz.
MattDMo

13
@MattDMo „Ktoś powiedział, że to źle” nie jest tak naprawdę argumentem.
Casey

4
@emodendroket Rozumiem, że nie wiesz, kim jest Douglas Crockford .
MattDMo

13
@MattDMo Jestem w pełni świadomy tego, kim on jest, ale on jest istotą ludzką, a nie bogiem.
Casey

2
@tggagne: Co mogę zrobić, aby działał w wielu domenach? (ładowanie skryptu z http://web.archive.org/web/20140905044059/http://www.howtocreate.co.uk/operaStuff/userjs/aagmfunctions.js)
user2284570

21

Lub zamiast włączać go w czasie wykonywania, użyj skryptu do konkatenacji przed przesłaniem.

Używam zębatek (nie wiem, czy są inne). Tworzysz swój kod JavaScript w osobnych plikach i dołączasz komentarze, które są przetwarzane przez silnik Sprockets. W celu programowania można dołączać pliki sekwencyjnie, a następnie w celu ich scalenia w środowisku produkcyjnym ...

Zobacz też:


Browserify jest znacznie bardziej popularny niż zębatki.
Dan Dascalescu,

18

Miałem prosty problem, ale zaskoczyły mnie odpowiedzi na to pytanie.

Musiałem użyć zmiennej (myVar1) zdefiniowanej w jednym pliku JavaScript (myvariables.js) w innym pliku JavaScript (main.js).

Zrobiłem to w następujący sposób:

Załadowałem kod JavaScript do pliku HTML, w odpowiedniej kolejności, najpierw myvariables.js, a następnie main.js:

<html>
    <body onload="bodyReady();" >

        <script src="myvariables.js" > </script>
        <script src="main.js" > </script>

        <!-- Some other code -->
    </body>
</html>

Plik: myvariables.js

var myVar1 = "I am variable from myvariables.js";

Plik: main.js

// ...
function bodyReady() {
    // ...
    alert (myVar1);    // This shows "I am variable from myvariables.js", which I needed
    // ...
}
// ...

Jak widzieliście, użyłem zmiennej w jednym pliku JavaScript w innym pliku JavaScript, ale nie musiałem uwzględniać jednej w innym. Musiałem tylko upewnić się, że pierwszy plik JavaScript załadowany przed drugim plikiem JavaScript oraz zmienne pierwszego pliku JavaScript są automatycznie dostępne w drugim pliku JavaScript.

To uratowało mi dzień. Mam nadzieję, że to pomoże.


Problem z tą odpowiedzią polega na tym, że tak nie jest import. Potrzebujesz pliku HTML, aby przenieść rzeczy z jednego pliku js do drugiego.
Cannicide

Bardzo prawdziwe. Oto niektóre inne odpowiedzi na to. Jednak miałem dwa pliki JS, jeden powinien używać innych zmiennych JS. Brak node.js, next.js, express.js itp., Więc import LUB wymaganie nie będzie działać, po prostu miałem zwykłe pliki .js.
Manohar Reddy Poreddy

Zrozumiałe jest jednak pytanie o import pliku javascript w innym pliku, a nie tylko dostęp do jego zmiennych za pomocą pliku HTML. Załóżmy na przykład, że tworzysz funkcjonalność przycisku i używasz trzech różnych plików js, jednego do obsługi kliknięć, jednego do obsługi aktywowania, jednego do wywołań zwrotnych dla pozostałych dwóch. Przydałoby się zaimportować pozostałe dwa js do samego trzeciego js i mieć tylko jeden <script>znacznik. Może to pomóc w organizacji. Ta odpowiedź po prostu nie jest pytaniem i nie jest idealna w tym kontekście.
Cannicide

Kiedy szukałem rozwiązania mojego problemu, szukałem właściwej drogi, jednak Google uważa, że ​​jest to właściwa strona, więc wylądowałem tutaj. Dlatego tutaj napisałem swoją odpowiedź. Wygląda na to, że zrobiłem dobrze, z powodu 15 pozytywnych opinii powyżej. Aby nie marnować czasu innych ludzi, położyłem wyraźny początek na moim scenariuszu - umieściłem kod HTML na pierwszym miejscu - co wygląda na pomocne, ponieważ odpowiedziałeś i powiedziałeś, że HTML nie jest twoim scenariuszem. Mam nadzieję, że wyjaśnione.
Manohar Reddy Poreddy

Tak - rzeczywiście wygląda na to, że niektórzy szukali nieco innego problemu, dla którego jest to rozwiązanie, i znaleźli to. Chociaż niekoniecznie jest to odpowiedź na to pytanie, zdecydowanie jest to odpowiedź na inne podobne pytanie. W związku z tym dobrze jest mieć tę odpowiedź tutaj, ponieważ pomaga ona tym, którzy jej szukali. Dzięki za odpowiedź, która to wyjaśniła.
Cannicide,

16

Jeśli używasz pracowników sieci Web i chcesz uwzględnić dodatkowe skrypty w zakresie pracownika, inne odpowiedzi na temat dodawania skryptów dohead znacznika itp. Nie będą działać.

Na szczęście pracownicy sieci mają swoją własną importScriptsfunkcję, która jest funkcją globalną w zakresie pracownika sieci, natywną dla samej przeglądarki, ponieważ jest częścią specyfikacji .

Alternatywnie, ponieważ wyróżnia się druga najwyżej oceniona odpowiedź na twoje pytanie , RequireJS może również obsługiwać dołączanie skryptów wewnątrz procesu roboczego (prawdopodobnie wywołuje importScriptssię, ale z kilkoma innymi przydatnymi funkcjami).


16

W nowoczesnym języku ze sprawdzeniem, czy skrypt został już załadowany, byłoby to:

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

Użycie (asynchronizacja / oczekiwanie):

try { await loadJs("https://.../script.js") } 
catch(error) {console.log(error)}

lub

await loadJs("https://.../script.js").catch(err => {})

Zastosowanie (obietnica):

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

Miły. Dobre rozwiązanie.
Naftali alias Neal

Zwięzły i wymaga tylko ES5 i elegancko unika formalnego oddzwaniania. Dziękuję Dmitry!
Eureka

Wow, działa w przeglądarce bez serwera. pi.js = po prostu var pi = 3.14. wywołaj funkcję loadJS () przezloadJs("pi.js").then(function(){ console.log(pi); });
zipzit

14

@importSkładni CSS dla osiągnięcia podobnego JavaScript importowanie jest możliwe przy użyciu narzędzia takiego jak mieszanka poprzez ich szczególny .mixtyp pliku (patrz tutaj ). Wyobrażam sobie, że aplikacja po prostu korzysta z jednej z wyżej wymienionych metod, chociaż nie wiem.

Z dokumentacji Mixture dotyczącej .mixplików:

Pliki mix to po prostu pliki .js lub .css z .mix. w nazwie pliku. Plik mieszany po prostu rozszerza funkcjonalność zwykłego pliku stylu lub skryptu i umożliwia importowanie i łączenie.

Oto przykładowy .mixplik, który łączy wiele .jsplików w jeden:

// scripts-global.mix.js
// Plugins - Global

@import "global-plugins/headroom.js";
@import "global-plugins/retina-1.1.0.js";
@import "global-plugins/isotope.js";
@import "global-plugins/jquery.fitvids.js";

Mikstura generuje to jako, scripts-global.jsa także jako wersja zminimalizowana (scripts-global.min.js ).

Uwaga: nie jestem w żaden sposób związany z Mixture, poza wykorzystywaniem go jako narzędzia programistycznego front-end. Natknąłem się na to pytanie, widząc.mix plik JavaScript w akcji (w jednym z bojlerów Mixture) i byłem przez to nieco zdezorientowany („możesz to zrobić?” Pomyślałem sobie). Potem zdałem sobie sprawę, że był to plik specyficzny dla aplikacji (nieco rozczarowujący, zgodził się). Niemniej jednak doszedł do wniosku, że wiedza ta może być pomocna dla innych.

AKTUALIZACJA : Mieszanina jest teraz darmowa (offline).

AKTUALIZACJA : Mieszanina jest teraz wycofana. Stare wersje mieszanin są nadal dostępne


Byłoby świetnie, gdyby był to moduł węzła.
b01

@ b01 Brzmi jak wyzwanie;) Gdybym tylko miał czas ... może ktoś inny?
Isaac Gregson,


13

Moją zwykłą metodą jest:

var require = function (src, cb) {
    cb = cb || function () {};

    var newScriptTag = document.createElement('script'),
        firstScriptTag = document.getElementsByTagName('script')[0];
    newScriptTag.src = src;
    newScriptTag.async = true;
    newScriptTag.onload = newScriptTag.onreadystatechange = function () {
        (!this.readyState || this.readyState === 'loaded' || this.readyState === 'complete') && (cb());
    };
    firstScriptTag.parentNode.insertBefore(newScriptTag, firstScriptTag);
}

Działa świetnie i nie używa dla mnie przeładowywania stron. Próbowałem metody AJAX (jedna z pozostałych odpowiedzi), ale nie wydaje mi się, aby działała tak ładnie.

Oto wyjaśnienie, w jaki sposób działa kod dla tych, którzy są ciekawi: zasadniczo tworzy nowy znacznik skryptu (po pierwszym) adresu URL. Ustawia go w tryb asynchroniczny, aby nie blokował reszty kodu, ale wywołuje wywołanie zwrotne, gdy readyState (stan treści do załadowania) zmieni się na „załadowany”.


13

Chociaż te odpowiedzi są świetne, istnieje proste „rozwiązanie”, które istniało od czasu załadowania skryptu i obejmie 99,999% przypadków użycia większości ludzi. Wystarczy dołączyć skrypt, którego potrzebujesz, przed skryptem, który tego wymaga. W przypadku większości projektów ustalenie, które skrypty są potrzebne i w jakiej kolejności, nie zajmuje dużo czasu.

<!DOCTYPE HTML>
<html>
    <head>
        <script src="script1.js"></script>
        <script src="script2.js"></script>
    </head>
    <body></body>
</html>

Jeśli skrypt2 wymaga skryptu 1, jest to naprawdę najłatwiejszy sposób na zrobienie czegoś takiego. Jestem bardzo zaskoczony, że nikt tego nie podniósł, ponieważ jest to najbardziej oczywista i najprostsza odpowiedź, która będzie obowiązywać w prawie każdym przypadku.


1
Zgaduję, że dla wielu osób, w tym dla mnie, potrzebujemy dynamicznej nazwy pliku.
Stuart McIntyre,

1
@StuartMcIntyre w takim przypadku ustaw atrybut src tagu skryptu w czasie wykonywania i poczekaj na zdarzenie onload (oczywiście w tym przypadku są lepsze rozwiązania w 2019 r.).
KthProg,

12

Napisałem prosty moduł, który automatyzuje zadanie importowania / włączania skryptów modułów w JavaScript. Szczegółowe objaśnienie kodu znajduje się w blogu JavaScript wymagają / importu / włączenia modułów .

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

require('ivar.util.string');
require('ivar.net.*');
require('ivar/util/array.js');
require('http://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js');

ready(function(){
    //Do something when required scripts are loaded
});

    //--------------------

var _rmod = _rmod || {}; //Require module namespace
_rmod.LOADED = false;
_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;
    }
};

_.rmod = namespaceToUri = function(script_name, url) {
    var np = script_name.split('.');
    if (np.getLast() === '*') {
        np.pop();
        np.push('_all');
    }

    if(!url)
        url = '';

    script_name = np.join('.');
    return  url + np.join('/')+'.js';
};

//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 uri = '';
    if (script_name.indexOf('/') > -1) {
        uri = script_name;
        var lastSlash = uri.lastIndexOf('/');
        script_name = uri.substring(lastSlash+1, uri.length);
    } 
    else {
        uri = _rmod.namespaceToUri(script_name, ivar._private.libpath);
    }

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

10

Ten skrypt doda plik JavaScript do góry dowolnego innego <script>tagu:

(function () {
    var li = document.createElement('script'); 
    li.type = 'text/javascript'; 
    li.src= "http://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"; 
    li.async=true; 
    var s = document.getElementsByTagName('script')[0]; 
    s.parentNode.insertBefore(li, s);
})();

10

Istnieje również Head.js . Bardzo łatwo jest poradzić sobie z:

head.load("js/jquery.min.js",
          "js/jquery.someplugin.js",
          "js/jquery.someplugin.css", function() {
  alert("Everything is ok!");
});

Jak widać, jest to łatwiejsze niż Require.js i tak wygodne jak $.getScriptmetoda jQuery . Ma również kilka zaawansowanych funkcji, takich jak ładowanie warunkowe, wykrywanie funkcji i wiele innych .


10

Istnieje wiele potencjalnych odpowiedzi na to pytanie. Moja odpowiedź opiera się oczywiście na wielu z nich. Właśnie z tym skończyłem po przeczytaniu wszystkich odpowiedzi.

Problem z $.getScriptjakimkolwiek innym rozwiązaniem, które wymaga oddzwonienia po zakończeniu ładowania, polega na tym, że jeśli masz wiele plików, które go używają i są od siebie zależne, nie możesz już wiedzieć, kiedy wszystkie skrypty zostały załadowane (po zagnieżdżeniu w wielu plikach).

Przykład:

plik3.js

var f3obj = "file3";

// Define other stuff

plik2.js:

var f2obj = "file2";
$.getScript("file3.js", function(){

    alert(f3obj);

    // Use anything defined in file3.
});

plik1.js:

$.getScript("file2.js", function(){
    alert(f3obj); //This will probably fail because file3 is only guaranteed to have loaded inside the callback in file2.
    alert(f2obj);

    // Use anything defined in the loaded script...
});

Masz rację, gdy mówisz, że możesz określić, że Ajax ma działać synchronicznie lub użyć XMLHttpRequest , ale wydaje się, że obecnym trendem jest wycofywanie synchronicznych żądań, więc możesz nie uzyskać pełnej obsługi przeglądarki teraz lub w przyszłości.

Możesz spróbować $.whensprawdzić tablicę odroczonych obiektów, ale teraz robisz to w każdym pliku, a plik2 zostanie uznany za załadowany, gdy tylko $.whenzostanie wykonany, a nie po wywołaniu zwrotnym, więc plik1 nadal kontynuuje wykonywanie przed załadowaniem pliku3 . To naprawdę ma ten sam problem.

Postanowiłem wrócić do tyłu zamiast do przodu. Dziękuję document.writeln. Wiem, że jest to tabu, ale dopóki jest właściwie używane, działa dobrze. Otrzymujesz kod, który można łatwo debugować, wyświetla poprawnie w DOM i może zapewnić porządek, w jaki zależności są ładowane poprawnie.

Oczywiście możesz użyć $ („body”). Append (), ale wtedy nie możesz już poprawnie debugować.

UWAGA: Musisz użyć tego tylko podczas ładowania strony, w przeciwnym razie pojawi się pusty ekran. Innymi słowy, zawsze umieszczaj to przed / poza dokumentem . Już . Nie testowałem tego przy użyciu po załadowaniu strony w zdarzeniu kliknięcia itp., Ale jestem pewien, że to się nie powiedzie.

Podobał mi się pomysł rozszerzenia jQuery, ale oczywiście nie musisz.

Przed wywołaniem document.writelnsprawdza, czy skrypt nie został już załadowany, oceniając wszystkie elementy skryptu.

Zakładam, że skrypt nie jest w pełni wykonany, dopóki document.readynie zostanie wykonane jego zdarzenie. (Wiem, że używanie document.readynie jest wymagane, ale wiele osób korzysta z niego, a obchodzenie się z tym jest zabezpieczeniem).

Po załadowaniu dodatkowych plików document.readywywołania zwrotne zostaną wykonane w niewłaściwej kolejności. Aby rozwiązać ten problem, gdy skrypt jest rzeczywiście ładowany, skrypt, który go zaimportował, jest ponownie importowany i wykonywanie jest zatrzymywane. Powoduje to, że plik źródłowy ma teraz document.readywywołanie zwrotne po dowolnym z importowanych skryptów.

Zamiast tego podejścia można było spróbować zmodyfikować jQuery readyList, ale wydawało się to gorszym rozwiązaniem.

Rozwiązanie:

$.extend(true,
{
    import_js : function(scriptpath, reAddLast)
    {
        if (typeof reAddLast === "undefined" || reAddLast === null)
        {
            reAddLast = true; // Default this value to true. It is not used by the end user, only to facilitate recursion correctly.
        }

        var found = false;
        if (reAddLast == true) // If we are re-adding the originating script we do not care if it has already been added.
        {
            found = $('script').filter(function () {
                return ($(this).attr('src') == scriptpath);
            }).length != 0; // jQuery to check if the script already exists. (replace it with straight JavaScript if you don't like jQuery.
        }

        if (found == false) {

            var callingScriptPath = $('script').last().attr("src"); // Get the script that is currently loading. Again this creates a limitation where this should not be used in a button, and only before document.ready.

            document.writeln("<script type='text/javascript' src='" + scriptpath + "'></script>"); // Add the script to the document using writeln

            if (reAddLast)
            {
                $.import_js(callingScriptPath, false); // Call itself with the originating script to fix the order.
                throw 'Readding script to correct order: ' + scriptpath + ' < ' + callingScriptPath; // This halts execution of the originating script since it is getting reloaded. If you put a try / catch around the call to $.import_js you results will vary.
            }
            return true;
        }
        return false;
    }
});

Stosowanie:

Plik 3:

var f3obj = "file3";

// Define other stuff
$(function(){
    f3obj = "file3docready";
});

Plik2:

$.import_js('js/file3.js');
var f2obj = "file2";
$(function(){
    f2obj = "file2docready";
});

Plik 1:

$.import_js('js/file2.js');

// Use objects from file2 or file3
alert(f3obj); // "file3"
alert(f2obj); // "file2"

$(function(){
    // Use objects from file2 or file3 some more.
    alert(f3obj); //"file3docready"
    alert(f2obj); //"file2docready"
});

Dokładnie tak stanowi obecnie akceptowana odpowiedź: po prostu za mało.
Cannicide

Główną różnicą jest to, że jeśli brakuje zależności, dodaje znacznik script dla zależności, a następnie dodaje znacznik script do pliku wywołującego i zgłasza błąd, który zatrzymuje wykonywanie. Powoduje to, że skrypty są przetwarzane i uruchamiane we właściwej kolejności i eliminuje potrzebę wywołań zwrotnych. Tak więc skrypt zależny zostanie załadowany i wykonany przed skryptem wywołującym podczas obsługi zagnieżdżania.
curlyhairedgenius

Jeśli chodzi o „obecny trend wydaje się polegać na wycofywaniu żądań synchronicznych”, jest to prawdą tylko dlatego, że wielu programistów nadużyło ich i zmusiło użytkowników do niepotrzebnego oczekiwania. Jeśli jednak żądanie jest naturalnie synchroniczne, podobnie jak instrukcja Include, prosty synchroniczny Ajax jest w porządku. Stworzyłem ogólną funkcję rozszerzenia JavaScript, która synchronicznie wykonuje funkcje, takie jak odczytywanie pliku, który nie jest częścią JavaScript, poprzez uruchomienie pliku „serwera” PHP i jest bardzo przydatna podczas pisania aplikacji przy użyciu JavaScript. Dla takiego Ajax nie jest potrzebny lokalny serwer WWW.
David Spector,

10

Istnieje kilka sposobów implementacji modułów w Javascript, oto 2 najpopularniejsze:

Moduły ES6

Przeglądarki nie obsługują jeszcze tego systemu modulowania, więc aby skorzystać z tej składni, musisz użyć programu pakującego, takiego jak webpack. Korzystanie z pakietu jest i tak lepsze, ponieważ może łączyć wszystkie różne pliki w jeden (lub kilka powiązanych) plików. Pozwoli to szybciej przesyłać pliki z serwera do klienta, ponieważ do każdego żądania HTTP jest przypisany jakiś narzut. W ten sposób zmniejszając ogólne żądanie HTTP, poprawiamy wydajność. Oto przykład modułów ES6:

// main.js file

export function add (a, b) {
  return a + b;
}

export default function multiply (a, b) {
  return a * b;
}


// test.js file

import {add}, multiply from './main';   // for named exports between curly braces {export1, export2}
                                        // for default exports without {}

console.log(multiply(2, 2));  // logs 4

console.log(add(1, 2));  // logs 3

CommonJS (używany w NodeJS)

Ten system modulacji jest używany w NodeJS. Zasadniczo dodajesz swój eksport do obiektu, który jest nazywany module.exports. Następnie możesz uzyskać dostęp do tego obiektu za pomocą require('modulePath'). Ważne jest, aby zdać sobie sprawę, że te moduły są buforowane, więc jeśli require()dany moduł zostanie dwukrotnie, zwróci on już utworzony moduł.

// main.js file

function add (a, b) {
  return a + b;
}

module.exports = add;  // here we add our add function to the exports object


// test.js file

const add = require('./main'); 

console.log(add(1,2));  // logs 3

9

Przyszedłem do tego pytania, ponieważ szukałem prostego sposobu na utrzymanie kolekcji przydatnych wtyczek JavaScript. Po obejrzeniu niektórych rozwiązań tutaj wymyśliłem:

  1. Skonfiguruj plik o nazwie „plugins.js” (lub extensions.js lub cokolwiek chcesz). Zachowaj pliki wtyczek razem z tym jednym plikiem głównym.

  2. plugins.js będzie miał tablicę o nazwie pluginNames[], którą będziemy iterować each(), a następnie doda <script>tag do nagłówka dla każdej wtyczki

//set array to be updated when we add or remove plugin files
var pluginNames = ["lettering", "fittext", "butterjam", etc.];

//one script tag for each plugin
$.each(pluginNames, function(){
    $('head').append('<script src="js/plugins/' + this + '.js"></script>');
});
  1. Ręcznie wywołaj tylko jeden plik w swojej głowie:
    <script src="js/plugins/plugins.js"></script>

ALE:

Mimo że wszystkie wtyczki są upuszczane do tagu head tak, jak powinny, nie zawsze są uruchamiane przez przeglądarkę po kliknięciu na stronę lub odświeżeniu.

Odkryłem, że bardziej niezawodne jest pisanie tagów skryptowych w PHP. Musisz go napisać tylko raz, a to tyle samo pracy, co wywołanie wtyczki za pomocą JavaScript.


@will, Twoje rozwiązanie wygląda na znacznie czystsze niż moje i obawiam się, że mogę mieć pewne błędy, jeśli użyję mojego, ponieważ korzysta z .append (). Aby z tego skorzystać, możesz wywołać tę funkcję tylko raz dla każdego pliku wtyczki, który chcesz dołączyć?
rgb_life
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.