Knotenstil für In-Browser-Javascript erforderlich?

85

Gibt es Bibliotheken für In-Browser-Javascript, die die gleiche Flexibilität / Modularität / Benutzerfreundlichkeit bieten wie die von Node require?

Um mehr Details zu liefern: Der Grund requireist so gut, dass es:

  1. Ermöglicht das dynamische Laden von Code von anderen Speicherorten (was meiner Meinung nach stilistisch besser ist, als den gesamten Code im HTML zu verknüpfen).
  2. Es bietet eine konsistente Schnittstelle zum Erstellen von Modulen
  3. Es ist für Module einfach, von anderen Modulen abhängig zu sein (so könnte ich beispielsweise eine API schreiben, für die jQuery erforderlich ist, damit ich sie verwenden kann jQuery.ajax()
  4. Geladenes Javascript hat einen Gültigkeitsbereich , was bedeutet , dass ich es laden var dsp = require("dsp.js");und darauf zugreifen kann dsp.FFT, was meine lokale Umgebung nicht beeinträchtigen würdevar FFT

Ich habe noch keine Bibliothek gefunden, die dies effektiv erledigt. Die Problemumgehungen, die ich normalerweise verwende, sind:

  • coffeescript-concat - es ist einfach genug, andere js zu benötigen, aber Sie müssen es kompilieren, was bedeutet, dass es für eine schnelle Entwicklung weniger geeignet ist (z. B. Erstellen von APIs im Test)

  • RequireJS - Es ist beliebt, unkompliziert und löst 1-3, aber mangelndes Scoping ist ein echter Deal-Breaker (ich glaube, head.js ist insofern ähnlich, als es kein Scoping gibt, obwohl ich nie Gelegenheit hatte, es zu verwenden. In ähnlicher Weise können LABjs.wait() Abhängigkeitsprobleme laden und besänftigen , aber es wird immer noch kein Scoping durchgeführt.

Soweit ich das beurteilen kann, scheint es viele Lösungen für das dynamische und / oder asynchrone Laden von Javascript zu geben, aber sie haben tendenziell die gleichen Probleme wie das Laden der Js aus HTML. Vor allem möchte ich eine Möglichkeit zum Laden von Javascript, die den globalen Namespace überhaupt nicht verschmutzt, mir aber dennoch das Laden und Verwenden von Bibliotheken ermöglicht (genau wie es der Knoten erfordert).

2020 UPDATE: Module sind jetzt Standard in ES6 und werden ab Mitte 2020 von den meisten Browsern nativ unterstützt . Module unterstützen sowohl das synchrone als auch das asynchrone Laden (mit Promise). Meine aktuelle Empfehlung lautet, dass die meisten neuen Projekte ES6-Module verwenden und einen Transpiler verwenden sollten, um für ältere Browser auf eine einzelne JS-Datei zurückzugreifen.

Generell ist die Bandbreite heute auch viel größer als damals, als ich diese Frage gestellt habe. In der Praxis könnten Sie sich also vernünftigerweise dafür entscheiden, immer einen Transpiler mit ES6-Modulen zu verwenden und sich eher auf die Code-Effizienz als auf das Netzwerk zu konzentrieren.

VORHERIGE BEARBEITUNG (oder wenn Sie ES6-Module nicht mögen): Seit dem Schreiben habe ich RequireJS (das jetzt eine viel klarere Dokumentation enthält) ausgiebig verwendet . RequireJS war meiner Meinung nach wirklich die richtige Wahl. Ich möchte klarstellen, wie das System für Menschen funktioniert, die genauso verwirrt sind wie ich:

Sie können requirein der täglichen Entwicklung verwenden. Ein Modul kann alles sein, was von einer Funktion zurückgegeben wird (normalerweise ein Objekt oder eine Funktion) und wird als Parameter festgelegt. Sie können Ihr Projekt auch zur Bereitstellung in eine einzige Datei kompilieren r.js(in der Praxis ist dies fast immer schneller, obwohl requireSkripte parallel geladen werden können).

Der Hauptunterschied zwischen RequireJS und Node-Style-Anforderungen wie browserify (ein cooles Projekt, das von tjameson vorgeschlagen wird) besteht in der Art und Weise, wie Module entworfen und benötigt werden:

  • RequireJS verwendet AMD (Async Module Definition). In AMD werden requireeine Liste der zu ladenden Module (Javascript-Dateien) und eine Rückruffunktion verwendet. Wenn jedes der Module geladen wurde, ruft es den Rückruf mit jedem Modul als Parameter für den Rückruf auf. Somit ist es wirklich asynchron und daher gut für das Web geeignet.
  • Der Knoten verwendet CommonJS. In CommonJS requirehandelt es sich um einen blockierenden Aufruf, der ein Modul lädt und als Objekt zurückgibt. Dies funktioniert gut für Node, da Dateien aus dem Dateisystem gelesen werden, was schnell genug ist, aber im Web schlecht funktioniert, da das synchrone Laden von Dateien viel länger dauern kann.

In der Praxis haben viele Entwickler Node (und damit CommonJS) verwendet, bevor sie AMD jemals sehen. Darüber hinaus werden viele Bibliotheken / Module für CommonJS (durch Hinzufügen von Elementen zu einem exportsObjekt) und nicht für AMD (durch Zurückgeben des Moduls von der defineFunktion) geschrieben. Daher möchten viele Node-Turned-Web-Entwickler CommonJS-Bibliotheken im Web verwenden. Dies ist möglich, da das Laden von einem <script>Tag blockiert. Lösungen wie browserify verwenden CommonJS (Node) -Module und schließen sie zusammen, damit Sie sie in Skript-Tags einfügen können.

Wenn Sie Ihr eigenes Projekt mit mehreren Dateien für das Web entwickeln, empfehle ich RequireJS dringend, da es sich wirklich um ein Modulsystem für das Web handelt (obwohl ich AMD bei fairer Offenlegung viel natürlicher finde als CommonJS). In letzter Zeit hat die Unterscheidung an Bedeutung verloren, da Sie mit RequireJS jetzt im Wesentlichen die CommonJS-Syntax verwenden können. Zusätzlich kann RequireJS verwendet werden, um AMD-Module in Node zu laden (obwohl ich Node-Amd-Loader bevorzuge ).

Alex Churchill
quelle
1
Hinweis RequireJS unterstützt tatsächlich Modularität und kann Umfang. Seit ich gefragt habe, habe ich es etwas ausführlicher benutzt. Meiner Meinung nach ist es voller Funktionen , erfordert jedoch viel Lesen der Dokumentation, um effektiv genutzt zu werden, und es erfordert eine Form von erstklassigem synchronem Laden, bevor es perfekt ist.
Alex Churchill
1
Ist die Unterscheidung, asynchron zu sein, so sinnvoll? Wann immer ich Code benötige, bin ich grundsätzlich daran gehindert, fortzufahren, weil er Funktionen definiert und so muss ich alles tun ...
Michael

Antworten:

17

Schauen Sie sich ender an . Es macht viel davon.

Auch browserify ist ziemlich gut. Ich habe Require-Kiss ¹ verwendet und es funktioniert. Es gibt wahrscheinlich andere.

Ich bin mir bei RequireJS nicht sicher. Es ist einfach nicht dasselbe wie bei Node. Möglicherweise treten Probleme beim Laden von anderen Speicherorten auf, dies funktioniert jedoch möglicherweise. Solange es eine Bereitstellungsmethode gibt oder etwas, das aufgerufen werden kann.

TL; DR - Ich würde browserify oder require-kiss empfehlen.


Aktualisieren:

1: require-kiss ist jetzt tot und der Autor hat es entfernt. Ich benutze RequireJS seitdem ohne Probleme. Der Autor von require-kiss schrieb pakmanager und pakman . Vollständige Offenlegung, ich arbeite mit dem Entwickler.

Persönlich mag ich RequireJS besser. Das Debuggen ist viel einfacher (Sie können separate Dateien in der Entwicklung und eine einzelne bereitgestellte Datei in der Produktion haben) und basiert auf einem soliden "Standard".

Beatgammit
quelle
Cool, habe Browserify noch nicht ausprobiert, aber es sieht genau so aus, wie ich es brauche.
Alex Churchill
Die Verbindung zu Require-Kiss scheint tot zu sein. Einfache (Neu-) Suche führte nirgendwo hin - wohin ging es?
Joel Purra
@JoelPurra - require-kiss wurde entfernt und durch pakmanager ersetzt. Ich empfehle require-js jetzt. Ich habe die Antwort aktualisiert.
Beatgammit
nette Antwort hier Mann :), hast du etwas dagegen, die Frage zu überprüfen, die ich gerade gestellt habe und die dieser ähnlich ist (aber gleichzeitig anders ist)? stackoverflow.com/questions/43237875/…
Webeng
16

Ich habe ein kleines Skript geschrieben, das das asynchrone und synchrone Laden von Javascript-Dateien ermöglicht, was hier von Nutzen sein könnte. Es hat keine Abhängigkeiten und ist kompatibel mit Node.js & CommonJS. Die Installation ist ziemlich einfach:

$ npm install --save @tarp/require

Fügen Sie dann einfach die folgenden Zeilen zu Ihrem HTML hinzu, um das Hauptmodul zu laden:

<script src="/node_modules/@tarp/require/require.min.js"></script>
<script>Tarp.require({main: "./scripts/main"});</script>

In Ihrem Hauptmodul (und natürlich in jedem Untermodul) können Sie es so verwenden, require()wie Sie es von CommonJS / NodeJS kennen. Die vollständigen Dokumente und den Code finden Sie auf GitHub .

Torben
quelle
1
Das ist unglaublich cool. Danke, dass du das geschrieben hast!
OldTimeGuitarGuy
Vielen Dank, @OldTimeGuitarGuy :)
Torben
10

Eine Variation der großartigen Antwort von Ilya Kharlamov mit etwas Code, damit es mit Chrome-Entwicklertools gut funktioniert.

//
///- REQUIRE FN
// equivalent to require from node.js
function require(url){
    if (url.toLowerCase().substr(-3)!=='.js') url+='.js'; // to allow loading without js suffix;
    if (!require.cache) require.cache=[]; //init cache
    var exports=require.cache[url]; //get from cache
    if (!exports) { //not cached
            try {
                exports={};
                var X=new XMLHttpRequest();
                X.open("GET", url, 0); // sync
                X.send();
                if (X.status && X.status !== 200)  throw new Error(X.statusText);
                var source = X.responseText;
                // fix (if saved form for Chrome Dev Tools)
                if (source.substr(0,10)==="(function("){ 
                    var moduleStart = source.indexOf('{');
                    var moduleEnd = source.lastIndexOf('})');
                    var CDTcomment = source.indexOf('//@ ');
                    if (CDTcomment>-1 && CDTcomment<moduleStart+6) moduleStart = source.indexOf('\n',CDTcomment);
                    source = source.slice(moduleStart+1,moduleEnd-1); 
                } 
                // fix, add comment to show source on Chrome Dev Tools
                source="//@ sourceURL="+window.location.origin+url+"\n" + source;
                //------
                var module = { id: url, uri: url, exports:exports }; //according to node.js modules 
                var anonFn = new Function("require", "exports", "module", source); //create a Fn with module code, and 3 params: require, exports & module
                anonFn(require, exports, module); // call the Fn, Execute the module
                require.cache[url]  = exports = module.exports; //cache obj exported by module
            } catch (err) {
                throw new Error("Error loading module "+url+": "+err);
            }
    }
    return exports; //require returns object exported by module
}
///- END REQUIRE FN
Lucio M. Tato
quelle
Danke Lucio! Ich habe lange nach einer solchen Minimallösung gesucht. Ich habe es erweitert, um relative Pfade zu unterstützen. Siehe unten.
Trausti Kristjansson
9

Mir ist klar, dass es Anfänger geben kann, die ihren Code organisieren möchten. Dies ist 2020 , und wenn Sie eine modulare JS-App in Betracht ziehen, sollten Sie sofort mit npm und Webpack beginnen .

Hier sind ein paar einfache Schritte, um loszulegen:

  1. Führen Sie npm init -yin Ihrem Projektstamm aus, um ein npm-Projekt zu initialisieren
  2. Laden Sie den Webpack-Modul-Bundler herunter: npm install webpack webpack-cli
  3. Erstellen Sie eine index.html-Datei:
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>App</title>
</head>
<body>

    <script src="_bundle.js"></script>
</body>
</html>

_bundle.jsAchten Sie besonders auf die Datei - dies ist eine endgültige JS-Datei, die vom Webpack generiert wird. Sie werden sie nicht direkt ändern (lesen Sie weiter).

  1. Erstellen Sie ein, <project-root>/app.jsin das Sie andere Module importieren:
const printHello = require('./print-hello');

printHello();
  1. Erstellen Sie ein Beispielmodul print-hello.js:
module.exports = function() {
    console.log('Hello World!');
}
  1. Erstellen Sie eine <project-root>/webpack.config.jsund kopieren Sie Folgendes:
var path = require('path');

module.exports = {
  entry: './app.js',
  output: {
    path: path.resolve(__dirname),
    filename: '_bundle.js'
  }
};

Im obigen Code gibt es 2 Punkte:

  • In den Eintrag app.jsschreiben Sie Ihren JS-Code. Es werden andere Module wie oben gezeigt importiert.
  • Die Ausgabe _bundle.jsist Ihr endgültiges Bundle, das vom Webpack generiert wird. Dies ist, was Ihr HTML am Ende sehen wird.

-7. Öffnen Sie Ihre package.jsund ersetzen Sie sie scriptsdurch den folgenden Befehl:

  "scripts": {
    "start": "webpack --mode production -w"
  },
  1. Führen Sie schließlich die Skriptüberwachung aus app.jsund generieren Sie die _bundle.jsDatei, indem Sie Folgendes ausführen : npm start.
  2. Viel Spaß beim Codieren!
Ilyas Assainov
quelle
1
im Jahr 2020 sollte dies als die richtige Antwort markiert werden
p13rnd
5
(function () {
    // c is cache, the rest are the constants
    var c = {},s="status",t="Text",e="exports",E="Error",r="require",m="module",S=" ",w=window;
    w[r]=function R(url) {
        url+=/.js$/i.test(url) ? "" : ".js";// to allow loading without js suffix;
        var X=new XMLHttpRequest(),module = { id: url, uri: url }; //according to the modules 1.1 standard
        if (!c[url])
            try {
                X.open("GET", url, 0); // sync
                X.send();
                if (X[s] && X[s] != 200) 
                    throw X[s+t];
                Function(r, e, m, X['response'+t])(R, c[url]={}, module); // Execute the module
                module[e] && (c[url]=module[e]);
            } catch (x) {
                throw w[E](E+" in "+r+": Can't load "+m+S+url+":"+S+x);
            }
        return c[url];
    }
})();

Wegen der Blockierung besser nicht in der Produktion verwenden. (In node.js ist require () ein blockierender Aufruf gut).

Ilya Kharlamov
quelle
sollte nicht "exportiert: {}" eine Eigenschaft von "Modul" sein? und der Aufruf sei (R, module.exports, module)
Lucio M. Tato
@ LucioM.Tato Ich bin nicht sicher, kann keine Erwähnung von module.exports im Modul 1.1-Standard sehen . Sie können require (module.id) jederzeit aufrufen, um die Exporte zu erhalten
Ilya Kharlamov
Ja. Sie haben Recht, ich habe über die Implementierung von node.js-Modulen nachgedacht. Ich habe in Ihrer wirklich guten Antwort einige Änderungen am Code hinzugefügt, damit er mit Chrome Dev Tools gut funktioniert (ich verwende ihn als Debug-Zeit-IDE). Ich werde den Code als weitere Antwort auf diese Frage veröffentlichen, falls dies für jemand anderen nützlich ist.
Lucio M. Tato
1

Webmake bündelt Node- ähnliche Module für den Browser. Probieren Sie es aus.

Mariusz Nowak
quelle
1

Require-Stub - Bietet knotenkonform requireim Browser, löst sowohl Module als auch relative Pfade auf. Verwendet eine ähnliche Technik wie TKRequire (XMLHttpRequest). Der resultierende Code ist vollständig browserfähig und require-stubkann als Ersatz für dienen watchify.

dy_
quelle
0

Hier ist eine Erweiterung der fantastischen Antwort von Lucio M. Tato, die das rekursive Laden von Modulen mit relativen Pfaden ermöglicht.

Hier ist ein Github-Projekt, in dem die Lösung untergebracht ist, und ein Beispiel für deren Verwendung:

https://github.com/trausti/TKRequire.js

Fügen Sie zur Verwendung von TKRequire.js die folgende Zeile in Ihre Kopfzeile ein

<script type = "text / javascript" src = "./ TKRequire.js"> </ script>

Laden Sie dann Module wie in node.js:

var MyModule = require ("./ relative / path / to / MyModule.js");

Trausti Kristjansson
quelle
Danke Trausti. Wenn Sie Javascript verwenden, sollten Sie github.com/luciotato/LiteScript (Beta) überprüfen . PD: Welches Level von CC Saga spielst du gerade? : P
Lucio M. Tato
Links sind tot. Wie können wir uns Ihren Code ansehen?
PA.