it-swarm.dev

Qual è la differenza tra Require.js e la semplice creazione di un elemento <script> nel DOM?

Qual è la differenza tra l'uso di Require.JS AMD semplicemente creando un elemento <script> nel DOM?

La mia comprensione di Require.JS è che offre la possibilità di caricare le dipendenze, ma questo non può essere fatto semplicemente creando un elemento <script> che carica il file JS esterno necessario?

Ad esempio, supponiamo di avere la funzione doStuff(), che richiede la funzione needMe(). doStuff() è nel file esterno do_stuff.js, mentre needMe() si trova nel file esterno need_me.js.

Facendo questo il modo Require.JS:

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

Facendo questo semplicemente creando un elemento script:

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
}

Entrambi questi lavori. Tuttavia, la seconda versione non richiede che io carichi tutta la libreria Require.js. Non vedo davvero alcuna differenza funzionale ...

137
maxedison

Ecco l'articolo di Nizza su ajaxian.com sul perché usarlo:

RequireJS: caricamento asincrono di JavaScript

  • una sorta di # include/import/require
  • possibilità di caricare dipendenze nidificate
  • facilità d'uso per lo sviluppatore ma supportata da uno strumento di ottimizzazione che aiuta la distribuzione
43
Sarfraz

Quali vantaggi offre Require.JS rispetto alla semplice creazione di un elemento nel DOM? 

Nel tuo esempio, stai creando il tag script in modo asincrono, il che significa che la funzione needMe() verrebbe invocata prima il file need_me.js ha terminato il caricamento. Ciò si traduce in eccezioni non rilevate in cui la funzione non è definita.

Invece, per far funzionare effettivamente ciò che stai suggerendo, dovresti fare qualcosa del genere:

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

}

Probabilmente, potrebbe essere o non essere il caso di utilizzare un gestore di pacchetti come RequireJS o di utilizzare una strategia puramente JavaScript come dimostrato sopra. Mentre la tua applicazione Web può caricarsi più velocemente, il richiamo di funzionalità e funzionalità sul sito sarebbe più lento in quanto comporterebbe l'attesa di caricamento delle risorse prima che l'azione possa essere eseguita.

Se un'applicazione Web è costruita come app a pagina singola, considera che le persone non ricaricheranno la pagina molto spesso. In questi casi, il precaricamento di tutto contribuirebbe a rendere l'esperienza più veloce quando effettivamente usando l'app. In questi casi, hai ragione, si può semplicemente caricare tutte le risorse semplicemente includendo i tag di script nella testa o nel corpo della pagina.

Tuttavia, se si crea un sito Web o un'applicazione Web che segue il modello più tradizionale in cui una transizione da una pagina all'altra, causando il ricaricamento delle risorse, un approccio di caricamento lento potrebbe accelerare queste transizioni.

52
jmort253

Alcuni altri motivi molto specifici per cui l'uso di RequireJS ha senso:

  1. Gestire le proprie dipendenze si riduce rapidamente per progetti considerevoli.
  2. Puoi avere tanti piccoli file quanti ne vuoi e non devi preoccuparti di tenere traccia delle dipendenze o di caricare l'ordine.
  3. RequireJS rende possibile scrivere un'intera app modulare senza toccare l'oggetto finestra. 

Tratto da i commenti di rmurphey qui in questo Gist .

Gli strati di astrazione possono essere un incubo per imparare e adattarsi, ma quando serve a uno scopo e lo fa bene, ha senso.

9

Ecco un esempio più concreto.

Sto lavorando a un progetto con 60 file. Abbiamo 2 diverse modalità di eseguirlo.

  1. Carica una versione concatenata, 1 file di grandi dimensioni. (Produzione)

  2. Carica tutti i 60 file (sviluppo)

Stiamo usando un caricatore, quindi abbiamo solo uno script nella pagina web 

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

Per impostazione predefinita, la modalità # 1 (caricamento del file concatenato di grandi dimensioni). Per eseguire la modalità in # 2 (file separati) impostiamo alcuni flag. Potrebbe essere qualsiasi cosa. Una chiave nella stringa di query. In questo esempio lo facciamo

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

loader.js assomiglia a questo

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

Lo script di compilazione è solo un file .sh che assomiglia a questo

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

eccetera...

Se viene aggiunto un nuovo file, utilizzeremo probabilmente la modalità # 2 poiché stiamo sviluppando, dobbiamo aggiungere una riga injectScript("somenewfile.js") a loader.js

Successivamente, per la produzione, dobbiamo aggiungere somenewfile.js al nostro script di compilazione. Un passo che spesso dimentichiamo e poi riceviamo messaggi di errore.

Passando ad AMD non è necessario modificare 2 file. Il problema di mantenere loader.js e lo script di build in sincronizzazione sparisce. Usando r.js o webpack può solo leggere il codice per creare large-concantinated.js

Può anche gestire le dipendenze, ad esempio abbiamo caricato 2 file lib1.js e lib2.js come questo

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

lib2 ha bisogno di lib1. Ha un codice interno che fa qualcosa di simile

lib1Api.installPlugin(...);

Ma poiché gli script iniettati vengono caricati in modo asincrono, non c'è alcuna garanzia che verranno caricati nell'ordine corretto. Questi 2 script non sono script AMD ma usando require.js possiamo dirgli le loro dipendenze

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

Il nostro modulo che usa lib1 lo facciamo

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

Ora require.js inietterà gli script per noi e non inietterà lib2 finché non verrà caricato lib1 poiché gli abbiamo detto che lib2 dipende da lib1. Inoltre non avvierà il nostro modulo che usa lib1 fino a quando sia lib2 sia lib1 abbiano caricato.

Ciò rende Nice lo sviluppo (nessuna fase di costruzione, nessuna preoccupazione per l'ordine di caricamento) e rende piacevole la produzione (non è necessario aggiornare uno script di build per ogni script aggiunto).

Come bonus aggiuntivo possiamo usare il plugin babel di webpack per eseguire babel sul codice per i browser più vecchi e ancora una volta non dobbiamo mantenere lo script di compilazione. 

Notare che se Chrome (il nostro browser di scelta) ha iniziato a supportare import per davvero, probabilmente passeremmo a quello per lo sviluppo ma questo non cambierebbe davvero nulla. Potremmo ancora usare il webpack per creare un file concatenato e potremmo usarlo per eseguire babel sul codice per tutti i browser.

Tutto ciò si ottiene non utilizzando tag script e utilizzando AMD

0
gman