Ich habe einen einfachen JavaScript-Bundler erstellt und es stellte sich heraus, dass er viel einfacher war, als ich erwartet hatte. Ich werde alles, was ich gelernt habe, in diesem Beitrag teilen.
Beim Schreiben großer Anwendungen empfiehlt es sich, unseren JavaScript-Quellcode in separate JS-Dateien aufzuteilen. Das Hinzufügen dieser Dateien zu Ihrem HTML-Dokument mithilfe mehrerer Skript-Tags führt jedoch zu neuen Problemen wie
Verschmutzung des globalen Namensraums.
Rennbedingungen.
Modul-Bundler kombinieren unseren Quellcode aus verschiedenen Dateien in einer großen Datei und helfen uns so, die Vorteile von Abstraktionen zu nutzen und gleichzeitig die Nachteile zu vermeiden.
Modul-Bundler erledigen dies im Allgemeinen in zwei Schritten.
Wie bereits erwähnt, hier
So würden wir das machen (JavaScript-Code voraus)
Erstellen Sie eine bundler.js-Datei in Ihrem Texteditor und fügen Sie den folgenden Code hinzu:
const bundler = (entry)=>{ const graph = createDependencyGraph(entry) const bundle = createBundle(graph) return bundle }
Die Bundler-Funktion ist der Haupteintrag unseres Bundlers. Es nimmt den Pfad zu einer Datei (Eintragsdatei) und gibt eine Zeichenfolge (das Bundle) zurück. Darin wird mithilfe der Funktion „createDependencyGraph“ ein Abhängigkeitsdiagramm generiert.
const createDependencyGraph = (path)=>{ const entryModule = createModule(path) /* other code */ }
Die Funktion createDependencyGraph übernimmt den Pfad zur Eintragsdatei. Es verwendet die Funktion „createModule“, um eine Moduldarstellung dieser Datei zu generieren.
let ID = 0 const createModule = (filename)=>{ const content = fs.readFileSync(filename) const ast = babylon.parse(content, {sourceType: “module”}) const {code} = babel.transformFromAst(ast, null, { presets: ['env'] }) const dependencies = [ ] const id = ID traverse(ast, { ImportDeclaration: ({node})=>{ dependencies.push(node.source.value) } } return { id, filename, code, dependencies } }
Die Funktion „createAsset“ nimmt den Pfad zu einer Datei und liest deren Inhalt in einen String. Diese Zeichenfolge wird dann in einen abstrakten Syntaxbaum analysiert. Ein abstrakter Syntaxbaum ist eine Baumdarstellung des Inhalts eines Quellcodes. Es kann mit dem DOM-Baum eines HTML-Dokuments verglichen werden. Dadurch ist es einfacher, einige Funktionen des Codes auszuführen, z. B. das Durchsuchen usw.
Wir erstellen einen Ast aus dem Modul mit dem Babylon-Parser.
Als nächstes konvertieren wir mit Hilfe des Babel-Core-Transpilers den Codeinhalt in eine Syntax vor ES2015, um browserübergreifende Kompatibilität zu gewährleisten.
Anschließend wird der ast mit einer speziellen Funktion von babel durchlaufen, um jede Importdeklaration unserer Quelldatei (Abhängigkeiten) zu finden.
Dann verschieben wir diese Abhängigkeiten (bei denen es sich um Zeichenfolgen mit relativen Dateipfaden handelt) in ein Abhängigkeitsarray.
Außerdem erstellen wir eine ID, um dieses Modul eindeutig zu identifizieren und
Schließlich geben wir ein Objekt zurück, das dieses Modul darstellt. Dieses Modul enthält eine ID, den Inhalt unserer Datei in einem String-Format, ein Array von Abhängigkeiten und den absoluten Dateipfad.
const createDependencyGraph = (path)=>{ const entryModule = createModule(path) const graph = [ entryModule ] for ( const module of graph) { module.mapping = { } module.dependencies.forEach((dep)=>{ let absolutePath = path.join(dirname, dep); let child = graph.find(mod=> mod.filename == dep) if(!child){ child = createModule(dep) graph.push(child) } module.mapping[dep] = child.id }) } return graph }
Zurück in unserer Funktion „createDependencyGraph“ können wir nun mit der Generierung unseres Diagramms beginnen. Unser Diagramm ist ein Array von Objekten, wobei jedes Objekt jede in unserer Anwendung verwendete Quelldatei darstellt.
Wir initialisieren unseren Graphen mit dem Eingabemodul und führen ihn dann in einer Schleife durch. Obwohl es nur ein Element enthält, fügen wir Elemente am Ende des Arrays hinzu, indem wir auf das Abhängigkeitsarray des Einstiegsmoduls (und anderer Module, die wir hinzufügen werden) zugreifen.
Das Abhängigkeitsarray enthält relative Dateipfade aller Abhängigkeiten eines Moduls. Das Array wird durchlaufen und für jeden relativen Dateipfad wird zunächst der absolute Pfad aufgelöst und zum Erstellen eines neuen Moduls verwendet. Dieses untergeordnete Modul wird an das Ende des Diagramms verschoben und der Prozess beginnt von vorne, bis alle Abhängigkeiten in Module konvertiert wurden.
Außerdem gibt jedes Modul ein Zuordnungsobjekt an, das einfach jeden relativen Abhängigkeitspfad der ID des untergeordneten Moduls zuordnet.
Für jede Abhängigkeit wird geprüft, ob bereits ein Modul vorhanden ist, um eine Duplizierung von Modulen und unendliche zirkuläre Abhängigkeiten zu verhindern.
Zum Schluss geben wir unseren Graphen zurück, der nun alle Module unserer Anwendung enthält.
Nachdem das Abhängigkeitsdiagramm erstellt wurde, umfasst die Generierung eines Bundles zwei Schritte
Wir müssen unsere Modulobjekte in Strings konvertieren, damit wir sie in die Datei bundle.js schreiben können. Wir tun dies, indem wir moduleString als leeren String initialisieren. Als Nächstes durchlaufen wir unser Diagramm und hängen jedes Modul als Schlüssel-Wert-Paare an die Modulzeichenfolge an, wobei die ID eines Moduls der Schlüssel und ein Array mit zwei Elementen ist: erstens der in die Funktion eingeschlossene Modulinhalt (um ihm, wie bereits erwähnt, den Gültigkeitsbereich zu geben). ) und zweitens ein Objekt, das die Zuordnung seiner Abhängigkeiten enthält.
const wrapModules = (graph)=>{ let modules = ‘’ graph.forEach(mod => { modules = `${http://mod.id}: [ function (require, module, exports) { ${mod.code} }, ${JSON.stringify(mod.mapping)}, ],`; }); return modules }
Außerdem ist zu beachten, dass die Funktion, die jedes Modul umschließt, ein require-, export- und module-Objekt als Argumente benötigt. Das liegt daran, dass diese nicht im Browser vorhanden sind, aber da sie in unserem Code erscheinen, werden wir sie erstellen und an diese Module übergeben.
Dies ist Code, der sofort ausgeführt wird, wenn das Bundle geladen wird. Er versorgt unsere Module mit den Objekten „require“, „module“ und „module.exports“.
const bundle = (graph)=>{ let modules = wrapModules(graph) const result = ` (function(modules) { function require(id) { const [fn, mapping] = modules[id]; function localRequire(name) { return require(mapping[name]); } const module = { exports : {} }; fn(localRequire, module, module.exports); return module.exports; } require(0); })({${modules}})`; return result; }
Wir verwenden einen sofort aufgerufenen Funktionsausdruck, der unser Modulobjekt als Argument verwendet. Darin definieren wir unsere Anforderungsfunktion, die anhand ihrer ID ein Modul von unserem Modulobjekt abruft.
Es erstellt eine für ein bestimmtes Modul spezifische localRequire-Funktion, um die Dateipfadzeichenfolge der ID zuzuordnen. Und ein Modulobjekt mit einer leeren Exporteigenschaft
Es führt unseren Modulcode aus, übergibt die Objekte localrequire, module und exports als Argumente und gibt dann module.exports zurück, genau wie es ein Node-JS-Modul tun würde.
Abschließend rufen wir require auf unserem Einstiegsmodul (Index 0) auf.
Um unseren Bundler zu testen, erstellen Sie im Arbeitsverzeichnis unserer Datei bundler.js eine index.js-Datei und zwei Verzeichnisse: ein src- und ein öffentliches Verzeichnis.
Erstellen Sie im öffentlichen Verzeichnis eine index.html-Datei und fügen Sie den folgenden Code in das Body-Tag ein:
Module bundler
const name = „David“
Standardname exportieren
also create a hello.js file and add the following code
Name aus „./name.js“ importieren
const hello = document.getElementById(“root”)
hello.innerHTML = „Hallo“ Name
Lastly in the index.js file of the root directory import our bundler, bundle the files and write it to a bundle.js file in the public directory
const createBundle = require("./bundler.js")
const run = (Ausgabe, Eingabe)=>{
let bundle = creatBundle(entry)
fs.writeFileSync(bundle, ‚utf-8‘)
}
run(./public/bundle.js“, „./src/hello.js“)
Open our index.html file in the browser to see the magic. In this post we have illustrated how a simple module bundler works. This is a minimal bundler meant for understanding how these technologies work behind the hood. please like if you found this insightful and comment any questions you may have.
Haftungsausschluss: Alle bereitgestellten Ressourcen stammen teilweise aus dem Internet. Wenn eine Verletzung Ihres Urheberrechts oder anderer Rechte und Interessen vorliegt, erläutern Sie bitte die detaillierten Gründe und legen Sie einen Nachweis des Urheberrechts oder Ihrer Rechte und Interessen vor und senden Sie ihn dann an die E-Mail-Adresse: [email protected] Wir werden die Angelegenheit so schnell wie möglich für Sie erledigen.
Copyright© 2022 湘ICP备2022001581号-3