Jaka jest różnica między Require.js a zwykłym utworzeniem elementu <script> w DOM? [Zamknięte]

138

Jaka jest różnica między użyciem Require.JS a zwykłym utworzeniem <script>elementu w DOM?

Rozumiem, że Require.JS umożliwia ładowanie zależności, ale czy nie można tego zrobić po prostu poprzez utworzenie <script>elementu, który ładuje niezbędny zewnętrzny plik JS?

Na przykład załóżmy, że mam funkcję doStuff(), która wymaga tej funkcji needMe(). doStuff()znajduje się w pliku zewnętrznym do_stuff.js, podczas gdy needMe()znajduje się w pliku zewnętrznym need_me.js.

Robiąc to w sposób Require.JS:

define(['need_me'],function(){
    function doStuff(){
        //do some stuff
        needMe();
        //do some more stuff
    }
});

Robi się to po prostu tworząc element skryptu:

function doStuff(){
    var scriptElement  = document.createElement('script');
    scriptElement.src = 'need_me.js';
    scriptElement.type = 'text/javascript';
    document.getElementsByTagName('head')[0].appendChild(scriptElement);

    //do some stuff
    needMe();
    //do some more stuff
}

Obie te metody działają. Jednak druga wersja nie wymaga ode mnie załadowania całej biblioteki Require.js. Naprawdę nie widzę żadnej różnicy funkcjonalnej ...

maxedison
źródło
1
a co z pamięcią podręczną przeglądarki, czy requirejs z tym przeszkadza?
Muhammad Umer
Otwieram to ponownie, ponieważ prosi o różnicę między dwiema bardzo podobnymi rzeczami. To może odpowiedzieć obiektywnie i tbh nie zobaczyć, gdzie więzi opinię pod nim.
RamenChef

Odpowiedzi:

43

Oto fajny artykuł na ajaxian.com, dlaczego go używać:

RequireJS: asynchroniczne ładowanie JavaScript

  • jakiś rodzaj # include / import / require
  • możliwość ładowania zagnieżdżonych zależności
  • łatwość użycia dla programistów, ale następnie wspierana przez narzędzie optymalizacyjne, które pomaga we wdrożeniu
Sarfraz
źródło
2
Czytałem je, ale teraz, gdy o tym myślę bardziej, zdaję sobie sprawę, że idei zagnieżdżonych zależności nie można osiągnąć po prostu pisząc znaczniki <script>. Dzięki.
maxedison
37
„łatwość obsługi dla programistów” nie może być dalsza od prawdy. To z pewnością stroma krzywa uczenia się dla Ciebie i każdego, kto przyjdzie pracować nad tym projektem.
Sahat Yalkabov
3
@TwilightPony Uważam, że nie jestem taki bystry i wymaganie nie było dla mnie trudne do zdobycia. Eliminuje konieczność martwienia się o zależności i przyspiesza działanie strony. Twój kod staje się bardziej wbudowany w programowanie po stronie serwera, ponieważ deklarujesz swoje zależności, które osobiście uważam za odświeżające i proste. Składnia była minimalna i zamknięta przez projekt, a następnie wyznacza mapę drogową dla produkcji, aby łatwo łączyć skrypty. Poza tym debugowanie działa jak statyczne deklaracje. Nie wiem, co jest łatwiejsze niż to. O wiele trudniej w drugą stronę, jak ja w drugą stronę.
Jason Sebring,
Walczę. Zwłaszcza w przypadku modułów, które próbują dołączyć się do obiektów globalnych. (Moduły React) ...
geilt
1
Komentarze na tej stronie faktycznie sprawiły, że poczułem, że należy uciekać od wymagań, a nie w ich kierunku. Zwłaszcza ten u dołu, który prowadzi do stevesouders.com/tests/require.php
Dave Kanter
52

Jakie zalety oferuje Require.JS w porównaniu do prostego tworzenia elementu w DOM?

W naszym przykładzie tag skryptu jest tworzony asynchronicznie, co oznacza, że needMe()funkcja zostanie wywołana przed zakończeniem ładowania pliku need_me.js. Powoduje to nieprzechwycone wyjątki, w których funkcja nie jest zdefiniowana.

Zamiast tego, aby to, co sugerujesz, zadziałało, musisz zrobić coś takiego:

function doStuff(){
    var scriptElement  = document.createElement('script');
    scriptElement.src = 'need_me.js';
    scriptElement.type = 'text/javascript';

    scriptElement.addEventListener("load", 
        function() { 
            console.log("script loaded - now it's safe to use it!");

            // do some stuff
            needMe();
            //do some more stuff

        }, false);

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

}

Prawdopodobnie najlepszym rozwiązaniem może być użycie menedżera pakietów, takiego jak RequireJS, lub wykorzystanie strategii opartej na czystym języku JavaScript, jak pokazano powyżej. Chociaż aplikacja sieci Web może ładować się szybciej, wywoływanie funkcji i funkcji w witrynie byłoby wolniejsze, ponieważ wymagałoby oczekiwania na załadowanie zasobów przed wykonaniem tej czynności.

Jeśli aplikacja internetowa jest zbudowana jako aplikacja jednostronicowa, weź pod uwagę, że ludzie nie będą często ponownie ładować strony. W takich przypadkach wstępne załadowanie wszystkiego pomoże sprawić, że doświadczenie będzie wydawać się szybsze podczas rzeczywistego korzystania z aplikacji. W takich przypadkach masz rację, możesz po prostu załadować wszystkie zasoby, po prostu umieszczając tagi skryptu w nagłówku lub treści strony.

Jednak w przypadku tworzenia witryny internetowej lub aplikacji sieci Web zgodnej z bardziej tradycyjnym modelem, w którym przechodzi się ze strony na stronę, powodując przeładowanie zasobów, podejście z opóźnionym ładowaniem może pomóc przyspieszyć te przejścia.

jmort253
źródło
10

Kilka innych bardzo wskazanych powodów, dla których używanie RequireJS ma sens:

  1. Zarządzanie własnymi zależnościami szybko się rozpada w przypadku dużych projektów.
  2. Możesz mieć tyle małych plików, ile chcesz i nie musisz się martwić o śledzenie zależności lub kolejności ładowania.
  3. RequireJS umożliwia napisanie całej, modułowej aplikacji bez dotykania obiektu okna.

Zaczerpnięte z komentarzy rmurpheya w tym streszczeniu .

Warstwy abstrakcji mogą być koszmarem, którego trzeba się nauczyć i do którego można się przystosować, ale kiedy służy celowi i robi to dobrze, ma po prostu sens.

girls_can_code_too
źródło
9
Nadal musisz zarządzać wszystkimi wymagającymi i zdefiniowanymi instrukcjami, plikami konfiguracyjnymi, kolizjami z innymi systemami i bibliotekami, które nie zaimplementowały specyfikacji AMD itp. Próbowałem użyć Require.JS w projekcie node-webkit i Require.JS walczył ze mną na każdym kroku ... Porównaj to z prostym zamawianiem skryptów w określony sposób ... Oczywiście, możesz leniwie ładować się z Require.JS, dlatego starałem się, aby to działało. :)
jmort253
Całkowicie zgadzam się z @ jmort253, na początku była to walka, ale teraz bardzo mi się podoba. Wszystkie trzy punkty są poprawne! A AMDowanie biblioteki nie powinno być takie trudne ... ani używać podkładki.
Legends
0

Oto bardziej konkretny przykład.

Pracuję w projekcie z 60 plikami. Mamy 2 różne tryby działania.

  1. Załaduj połączoną wersję, 1 duży plik. (Produkcja)

  2. Załaduj wszystkie 60 plików (programowanie)

Używamy programu ładującego, więc na stronie mamy tylko jeden skrypt

<script src="loader.js"></script>

Domyślnie jest to tryb nr 1 (ładowanie jednego dużego połączonego pliku). Aby uruchomić tryb in # 2 (oddzielne pliki), ustawiliśmy flagę. To może być wszystko. Klucz w ciągu zapytania. W tym przykładzie po prostu to robimy

<script>useDebugVersion = true;</script>
<script src="loader.js"></script>

loader.js wygląda mniej więcej tak

if (useDebugVersion) {
   injectScript("app.js");
   injectScript("somelib.js");
   injectScript("someotherlib.js");
   injectScript("anotherlib.js");
   ... repeat for 60 files ...
} else {
   injectScript("large-concatinated.js");
}

Skrypt budujący to po prostu plik .sh, który wygląda tak

cat > large-concantinated.js app.js somelib.js someotherlib.js anotherlib.js

itp...

Jeśli zostanie dodany nowy plik, prawdopodobnie będziemy używać trybu nr 2, ponieważ pracujemy nad rozwojem, musimy dodać injectScript("somenewfile.js")linię do loader.js

Później do produkcji musimy również dodać somenewfile.js do naszego skryptu kompilacji. Krok, o którym często zapominamy, a następnie otrzymujemy komunikaty o błędach.

Przechodząc na AMD nie musimy edytować 2 plików. Problem z utrzymaniem synchronizacji loader.js i skryptu kompilacji znika. Używając r.jslub webpackmoże po prostu odczytać kod do zbudowanialarge-concantinated.js

Potrafi również radzić sobie z zależnościami, na przykład mieliśmy 2 pliki lib1.js i lib2.js załadowane w ten sposób

injectScript("lib1.js");
injectScript("lib2.js");

lib2 wymaga lib1. Zawiera kod, który robi coś podobnego

lib1Api.installPlugin(...);

Ale ponieważ wstrzyknięte skrypty są ładowane asynchronicznie, nie ma gwarancji, że zostaną załadowane we właściwej kolejności. Te 2 skrypty nie są skryptami AMD, ale używając require.js możemy określić ich zależności

require.config({
    paths: {
        lib1: './path/to/lib1',
        lib2: './path/to/lib2',
    },
    shim: {
        lib1: {
            "exports": 'lib1Api',
        },
        lib2: {
            "deps": ["lib1"],
        },
    }
});

Robimy to w naszym module korzystającym z lib1

define(['lib1'], function(lib1Api) {
   lib1Api.doSomething(...);
});

Teraz require.js wstrzyknie skrypty za nas i nie wstrzyknie lib2, dopóki lib1 nie zostanie załadowana, ponieważ powiedzieliśmy, że lib2 zależy od lib1. Nie uruchomi również naszego modułu używającego lib1, dopóki nie załaduje się zarówno lib2, jak i lib1.

To sprawia, że ​​programowanie jest przyjemne (bez etapu kompilacji, bez martwienia się o kolejność ładowania) i sprawia, że ​​produkcja jest przyjemna (nie ma potrzeby aktualizowania skryptu kompilacji dla każdego dodanego skryptu).

Jako dodatkowy bonus możemy użyć wtyczki babel pakietu webpack, aby uruchomić babel na kodzie dla starszych przeglądarek i znowu nie musimy też utrzymywać tego skryptu budującego.

Zwróć uwagę, że gdyby Chrome (nasza wybrana przeglądarka) zaczął obsługiwać importnaprawdę, prawdopodobnie przełączylibyśmy się na to w celu programowania, ale to tak naprawdę niczego nie zmieni. Moglibyśmy nadal używać webpacka do tworzenia połączonego pliku i moglibyśmy go użyć do uruchomienia babel na kodzie dla wszystkich przeglądarek.

Wszystko to uzyskuje się nie używając tagów skryptów i używając AMD

gman
źródło