Detaillierte Erklärung der NodeJS-Modularität

Detaillierte Erklärung der NodeJS-Modularität

1. Einleitung

Wir wissen, dass Node.js basierend auf der CommonJS-Spezifikation modularisiert ist. Modularisierung ist ein unverzichtbares Werkzeug für komplexe Geschäftsszenarien. Vielleicht verwenden Sie es häufig, haben es aber nie systematisch verstanden. Deshalb werden wir heute über einige Dinge sprechen, die Sie über die Modularisierung von Node.js wissen müssen, und die Modularisierung von Node.js erkunden.

2. Haupttext

In Node.js sind zwei Module für die modulare Verwaltung integriert. Diese beiden Module sind auch zwei Schlüsselwörter, mit denen wir sehr vertraut sind: require und module. Integriert bedeutet, dass wir diese beiden Module im globalen Bereich verwenden können, ohne auf sie wie auf andere Module verweisen zu müssen.

Keine Notwendigkeit für require('require') oder require('module')

Es ist nicht schwierig, in Node.js auf ein Modul zu verweisen. Es ist ganz einfach:

const config = require('/Pfad/zur/Datei')

Tatsächlich führt dieser einfache Code jedoch insgesamt fünf Schritte aus:

Das Verständnis dieser fünf Schritte hilft uns, die Grundprinzipien der Modularität von Node.js zu verstehen und auch einige Fallstricke zu erkennen. Lassen Sie uns kurz zusammenfassen, was diese fünf Schritte bewirken:

  • Auflösen: Suchen Sie das zu referenzierende Zielmodul und generieren Sie einen absoluten Pfad.
  • Laden: Bestimmen Sie den Typ des zu referenzierenden Modulinhalts. Dies kann eine JSON-Datei, eine JS-Datei oder eine NODE-Datei sein.
  • Wrapping: Wie der Name schon sagt, wird das referenzierte Modul umschlossen. Durch die Verpackung erhalten Module einen privaten Umfang.
  • Auswerten: Das geladene Modul wird tatsächlich analysiert und verarbeitet.
  • Caching: Cache-Module, wodurch wir dasselbe Modul importieren können, ohne die obigen Schritte zu wiederholen.

Einige Schüler haben diese fünf Schritte möglicherweise verstanden und sind mit diesen Prinzipien vertraut, während andere möglicherweise mehr Zweifel haben. In jedem Fall werden im folgenden Inhalt die oben genannten Ausführungsschritte im Detail analysiert, in der Hoffnung, allen zu helfen, Fragen zu beantworten oder Wissen zu festigen und die Lücken zu schließen.

Übrigens kannst du bei Bedarf wie ich ein Experimentalverzeichnis aufbauen und im Anschluss an die Demo Experimente durchführen.

2.1 Was ist ein Modul?

Um Modularität zu verstehen, müssen Sie sich zunächst ansehen, was ein Modul ist.

Wir wissen, dass Dateien in Node.js Module sind. Wir haben gerade erwähnt, dass Module .js-, .json- oder .node-Dateien sein können. Indem wir sie referenzieren, können wir Toolfunktionen, Variablen, Konfigurationen usw. abrufen, aber wie ist ihre spezifische Struktur? Um das Modul, also die Struktur des Modulobjekts, anzuzeigen, führen Sie einfach den folgenden Befehl in der Kommandozeile aus:

~/Lernknoten $ Knoten

> Modul

Modul {

ID: '<repl>',

Exporte: {},

Elternteil: undefiniert,

Dateiname: null,

geladen: falsch,

Kinder: [],

Pfade: [ ... ] }

Es ist ersichtlich, dass ein Modul nur ein gewöhnliches Objekt ist, aber es gibt mehrere spezielle Attributwerte in der Struktur, die wir einzeln verstehen müssen. Einige Attribute wie ID, übergeordnetes Element, Dateiname und untergeordnetes Element müssen nicht einmal erklärt werden und können anhand ihrer wörtlichen Bedeutung verstanden werden.

Die folgenden Inhalte helfen Ihnen, die Bedeutung und Funktion dieser Felder zu verstehen.

2.2 Lösung

Nachdem wir ein allgemeines Verständnis davon haben, was Module sind, beginnen wir mit dem ersten Schritt, dem Auflösen, um das Prinzip der Modularisierung zu verstehen, d. h. wie Node.js das Zielmodul findet und den absoluten Pfad des Zielmoduls generiert.

Warum haben wir also gerade jetzt das Modulobjekt ausgedruckt, damit jeder die Struktur des Moduls versteht? Denn es gibt zwei Feldwerte – ID, Pfade und Auflösungsschritte – die eng miteinander verknüpft sind. Schauen wir es uns gemeinsam an.

Zuerst das ID-Attribut:

Jedes Modul verfügt über ein ID-Attribut, das normalerweise den vollständigen Pfad des Moduls darstellt. Node.js kann diesen Wert verwenden, um das Modul zu identifizieren und zu lokalisieren. Aber hier gibt es kein spezielles Modul, wir geben lediglich die Modulstruktur in der Befehlszeile aus, daher handelt es sich um den Standardwert <repl> (repl steht für interaktiver Interpreter).

Als nächstes folgt das Pfadattribut:

Was ist die Funktion dieses Pfadattributs? Node.js ermöglicht es uns, Module auf verschiedene Arten zu referenzieren, z. B. über relative Pfade, absolute Pfade und voreingestellte Pfade (die in Kürze erläutert werden). Angenommen, wir müssen auf ein Modul namens „find-me“ verweisen. Wie hilft uns require, dieses Modul zu finden?

erfordern('finde mich')

Lassen Sie uns ausdrucken, was sich in den Pfaden befindet:

~/Lernknoten $ Knoten
> Modulpfade
[ '/Benutzer/samer/learn-node/repl/node_modules',
  '/Benutzer/samer/learn-node/node_modules',
  '/Benutzer/samer/node_modules',
  '/Benutzer/node_modules',
  '/Knotenmodule',
  '/Benutzer/samer/.node_modules',
  „/Benutzer/samer/.node_libraries“,
  '/usr/local/Cellar/node/7.7.1/lib/node' ]
  

Ok, es handelt sich eigentlich um eine Reihe absoluter Systempfade. Diese Pfade stellen die möglichen Standorte aller Zielmodule dar und sind geordnet, was bedeutet, dass Node.js alle in den Pfaden aufgelisteten Pfade der Reihe nach durchsucht. Wenn das Modul gefunden wird, wird der absolute Pfad des Moduls zur späteren Verwendung ausgegeben.

Jetzt wissen wir, dass Node.js in diesem Verzeichnisstapel nach Modulen sucht. Wir versuchen, require('find-me') auszuführen, um das Find-Me-Modul zu finden. Da wir das Find-Me-Modul in keinem Verzeichnis abgelegt haben, kann Node.js das Zielmodul nach Durchlaufen aller Verzeichnisse nicht finden und meldet daher den Fehler „Modul 'find-me' kann nicht gefunden werden“. Dieser Fehler wird möglicherweise häufig angezeigt:

~/learn-node $ Knoten

> erfordern('finde mich')

Fehler: Modul „Find-Me“ kann nicht gefunden werden

bei Function.Module._resolveFilename (module.js:470:15)

bei Function.Module._load (module.js:418:25)

bei Module.require (module.js:498:17)

bei Bedarf (internal/module.js:20:19)

bei repl:1:1

bei ContextifyScript.Script.runInThisContext (vm.js:23:33)

bei REPLServer.defaultEval (repl.js:336:29)

an gebundener Stelle (domain.js:280:14)

bei REPLServer.runBound [als eval] (domain.js:293:12)

bei REPLServer.onLine (repl.js:533:10)

Jetzt können Sie versuchen, das Find-Me-Modul, auf das Sie verweisen müssen, in einem der oben genannten Verzeichnisse abzulegen. Hier erstellen wir ein node_modules-Verzeichnis und eine find-me.js-Datei, damit Node.js es finden kann:

~/Lernknoten $ mkdir Knotenmodule

~/learn-node $ echo "console.log('Ich habe mich nicht verlaufen');" > node_modules/find-me.js

~/Lernknoten $ Knoten

> erfordern('finde mich');

Ich bin nicht verloren

{}

>

Nach dem manuellen Erstellen der Datei find-me.js hat Node.js das Zielmodul gefunden. Wenn das Find-Me-Modul im lokalen node_modules-Verzeichnis von Node.js gefunden wird, wird natürlich nicht weiter in nachfolgenden Verzeichnissen gesucht.

Studenten mit Node.js-Entwicklungserfahrung werden feststellen, dass es beim Verweisen auf ein Modul nicht notwendig ist, eine genaue Datei anzugeben. Sie können das Zielmodul auch referenzieren, indem Sie auf das Verzeichnis verweisen, zum Beispiel:

~/learn-node $ mkdir -p node_modules/find-me

~/learn-node $ echo "console.log('Wieder gefunden.');" > node_modules/find-me/index.js

~/Lernknoten $ Knoten

> erfordern('finde mich');

Wieder gefunden.

{}

>

Die Datei index.js im Find-Me-Verzeichnis wird automatisch importiert.

Natürlich unterliegt dies Regelbeschränkungen. Der Grund, warum Node.js die Datei index.js im Find-Me-Verzeichnis finden kann, liegt darin, dass die Standardregel für den Modulimport darin besteht, nach der Datei index.js zu suchen, wenn der spezifische Dateiname fehlt. Wir können auch die Importregeln ändern (durch Modifizierung von package.json), zum Beispiel index -> main:

~/learn-node $ echo "console.log('Ich regiere');" > node_modules/find-me/main.js

~/learn-node $ echo '{ "name": "find-me-folder", "main": "main.js" }' > node_modules/find-me/package.json

~/learn-node $ Knoten

> erfordern('finde mich');

Ich regiere

{}

>

2.3, erfordern.lösen

Wenn Sie lediglich ein Modul in Ihr Projekt einführen möchten, ohne es sofort auszuführen, können Sie die Methode require.resolve verwenden, die der Methode require ähnelt, mit dem Unterschied, dass die Methode des eingeführten Moduls nicht ausgeführt wird:

> erfordern.lösen('finde mich');

„/Benutzer/samer/learn-node/node_modules/find-me/start.js“

> require.resolve('nicht da');

Fehler: Modul „not-there“ kann nicht gefunden werden

bei Function.Module._resolveFilename (module.js:470:15)

bei Function.resolve (internal/module.js:27:19)

bei repl:1:9

bei ContextifyScript.Script.runInThisContext (vm.js:23:33)

bei REPLServer.defaultEval (repl.js:336:29)

an gebundener Stelle (domain.js:280:14)

bei REPLServer.runBound [als eval] (domain.js:293:12)

bei REPLServer.onLine (repl.js:533:10)

bei emitOne (events.js:101:20)

bei REPLServer.emit (events.js:191:7)

>

Wie Sie sehen, druckt Node.js den vollständigen Pfad des Moduls, wenn das Modul gefunden wird. Wenn es nicht gefunden wird, wird ein Fehler gemeldet.

Nachdem wir nun wissen, wie Node.js nach Modulen sucht, schauen wir uns an, wie Node.js Module lädt.

2.4. Eltern-Kind-Abhängigkeit zwischen Modulen

Wir drücken die Referenzbeziehung zwischen Modulen als Eltern-Kind-Abhängigkeitsbeziehung aus.

Erstellen Sie einfach eine lib/util.js-Datei und fügen Sie eine console.log-Anweisung hinzu, um anzuzeigen, dass dies ein referenziertes Untermodul ist.

~/learn-node $ mkdir lib

~/learn-node $ echo "console.log('In util');" > lib/util.js

Geben Sie außerdem eine Zeile mit console.log-Anweisungen in index.js ein, um dies als übergeordnetes Modul zu identifizieren und auf die gerade erstellte lib/util.js als Untermodul zu verweisen.

~/learn-node $ echo "erfordern('./lib/util'); console.log('Im Index, übergeordnetes Element', Modul);" > index.js

Führen Sie index.js aus, um die Abhängigkeiten zwischen ihnen anzuzeigen:

~/learn-node $ Knotenindex.js

Im util

Im Index <ref *1> Modul {

Ausweis: '.',

Pfad: '/Users/samer/',

Exporte: {},

übergeordnetes Element: null,

Dateiname: '/Users/samer/index.js',

geladen: falsch,

Kinder: [

Modul {

ID: "/Benutzer/samer/lib/util.js",

Pfad: '/Users/samer/lib',

Exporte: {},

übergeordnet: [Rundschreiben *1],

Dateiname: '/Users/samer/lib/util.js',

geladen: wahr,

Kinder: [],

Pfade: [Array]

}

],

Pfade: [...]

}

Hier konzentrieren wir uns auf zwei Eigenschaften im Zusammenhang mit Abhängigkeiten: untergeordnete Elemente und übergeordnete Elemente.

Im gedruckten Ergebnis enthält das Feld „Kinder“ das importierte Modul „util.js“, was darauf hinweist, dass es sich bei util.js um ein Untermodul handelt, von dem index.js abhängt.

Wenn wir uns jedoch die übergeordnete Eigenschaft des Moduls util.js genauer ansehen, stellen wir fest, dass hier der Wert Circular erscheint. Der Grund dafür ist, dass beim Drucken der Modulinformationen eine zirkuläre Abhängigkeit generiert wird. Die übergeordneten Modulinformationen werden in den untergeordneten Modulinformationen gedruckt, und die untergeordneten Modulinformationen werden in den übergeordneten Modulinformationen gedruckt. Daher markiert Node.js es einfach als Circular.

Warum müssen wir Eltern-Kind-Abhängigkeiten verstehen? Da dies mit der Art und Weise zusammenhängt, wie Node.js zirkuläre Abhängigkeiten behandelt, wird es später ausführlich beschrieben.

Bevor wir uns mit dem Problem des Umgangs mit zirkulären Abhängigkeiten befassen, müssen wir zwei Schlüsselkonzepte verstehen: Exporte und Modulexporte.

2.5. Exporte, Modul.Exporte

Exporte:

Exports ist ein spezielles Objekt, das ohne Deklaration direkt als globale Variable in Node.js verwendet werden kann. Es handelt sich tatsächlich um einen Verweis auf module.exports. Durch Ändern der Exporte kann der Zweck der Änderung von module.exports erreicht werden.

Exports ist auch ein Eigenschaftswert in der gerade gedruckten Modulstruktur, aber die gerade gedruckten Werte sind alle leere Objekte, da wir in der Datei nicht damit gearbeitet haben. Jetzt können wir versuchen, ihm einfach einen Wert zuzuweisen:

// Fügen Sie am Anfang von lib/util.js eine neue Zeile exports.id = 'lib/util' hinzu.
  
// Fügen Sie am Anfang von index.js eine neue Zeile exports.id = 'index' hinzu.

Führen Sie index.js aus:

~/learn-node $ Knotenindex.js

Im Index Modul {

Ausweis: '.',

Exporte: { id: 'index' },

geladen: falsch,

... }

Im util-Modul {

ID: '/Benutzer/samer/learn-node/lib/util.js',

Exporte: { id: 'lib/util' },

Elternteil:

Modul {

Ausweis: '.',

Exporte: { id: 'index' },

geladen: falsch,

... },

geladen: falsch,

... }

Sie können sehen, dass die beiden gerade hinzugefügten ID-Attribute erfolgreich zum Exportobjekt hinzugefügt wurden. Wir können auch beliebige andere Attribute als die ID hinzufügen, genau wie beim Bedienen gewöhnlicher Objekte. Natürlich können wir Exporte auch in eine Funktion umwandeln, zum Beispiel:

Exporte = Funktion() {}

Modul.Export:

Das Objekt module.exports ist eigentlich das, was wir schließlich durch require erhalten. Wenn wir ein Modul schreiben, erhalten andere, die auf das Modul verweisen, den Wert, den wir module.exports zuweisen. Beispielsweise in Kombination mit der vorherigen Operation auf lib/util:

const util = erfordern('./lib/util');
  
console.log('UTIL:', util);
  
// Ausgabeergebnis UTIL: { id: 'lib/util' }

Da wir {id: 'lib/util'} gerade über das Exportobjekt an module.exports zugewiesen haben, ändert sich das Ergebnis von require entsprechend.

Jetzt haben wir ein allgemeines Verständnis davon, was Exporte und module.exporte sind, aber es gibt ein kleines Detail zu beachten, nämlich, dass das Laden von Modulen in Node.js ein synchroner Prozess ist.

Schauen wir uns noch einmal das geladene Attribut in der Modulstruktur an. Dieses Attribut gibt an, ob das Modul geladen wurde. Mit diesem Attribut lässt sich die Synchronisierung des Ladens von Node.js-Modulen leicht überprüfen.

Wenn das Modul geladen wurde, sollte der geladene Wert wahr sein. Aber bisher ist jedes Mal, wenn wir ein Modul drucken, sein Status „false“. Das liegt eigentlich daran, dass das Laden von Modulen in Node.js synchron erfolgt. Wenn wir den Ladevorgang nicht abgeschlossen haben (der Ladevorgang umfasst das Markieren des Moduls, einschließlich des Markierens des geladenen Attributs), ist das gedruckte Ergebnis standardmäßig „geladen: falsch“.

Wir verwenden setImmediate, um diese Informationen zu überprüfen:

// In index.js
setImmediate(() => {
  console.log('Das Modulobjekt index.js ist jetzt geladen!', Modul)
});
Klicken und ziehen, um zu verschieben. Das Modulobjekt index.js ist jetzt geladen! Modul {
  Ausweis: '.',
  Exporte: [Funktion],
  übergeordnetes Element: null,
  Dateiname: '/Users/samer/learn-node/index.js',
  geladen: wahr,
  Kinder:
   [Modul {
       ID: '/Benutzer/samer/learn-node/lib/util.js',
       Exporte: [Objekt],
       übergeordnetes Element: [Rundschreiben],
       Dateiname: '/Users/samer/learn-node/lib/util.js',
       geladen: wahr,
       Kinder: [],
       Pfade: [Objekt] } ],
  Pfade:
   [ '/Benutzer/samer/learn-node/node_modules',
     '/Benutzer/samer/node_modules',
     '/Benutzer/node_modules',
     '/Knotenmodule' ] }

Ok, da console.log nach Abschluss des Ladevorgangs abgelegt (markiert) wird, lautet der Ladestatus jetzt „geladen: true“. Dadurch wird vollständig überprüft, ob das Laden des Node.js-Moduls ein synchroner Prozess ist.

Nachdem wir nun etwas über Exporte, module.exporte und die Synchronisierung des Modulladens wissen, wollen wir einen Blick darauf werfen, wie Node.js zirkuläre Abhängigkeiten von Modulen handhabt.

2.6. Zirkuläre Modulabhängigkeit

Im obigen Inhalt haben wir gelernt, dass zwischen Modulen eine Eltern-Kind-Abhängigkeitsbeziehung besteht. Was macht Node.js, wenn zwischen Modulen eine zirkuläre Abhängigkeit auftritt? Angenommen, es gibt zwei Module, module1.js und module2.js, und sie verweisen wie folgt aufeinander:

// lib/module1.js
  
exporte.a = 1;
  
require('./module2'); // Hier referenzieren exports.b = 2;
exporte.c = 3;
  
// lib/module2.js
  
const Module1 = erfordern('./module1');
console.log('Modul1 ist hier teilweise geladen', Module1); // referenziere Modul1 und drucke es aus

Versuchen Sie, module1.js auszuführen. Sie können die Ausgabe sehen:

~/learn-node $ node lib/module1.js

Modul1 ist hier teilweise geladen { a: 1 }

Das Ergebnis gibt lediglich {a: 1} aus, während {b: 2, c: 3} fehlt. Bei genauerer Betrachtung von module1.js stellen wir fest, dass wir in der Mitte von module1.js, bevor exports.b = 2 und exports.c = 3 ausgeführt werden, einen Verweis auf module2.js hinzugefügt haben. Wenn wir diesen Ort den Ort nennen, an dem eine zirkuläre Abhängigkeit auftritt, dann ist das Ergebnis, das wir erhalten, die Eigenschaft, die exportiert wurde, bevor die zirkuläre Abhängigkeit auftritt. Dies basiert auch auf der Schlussfolgerung, dass das Laden von Modulen in Node.js ein synchroner Prozess ist, was wir oben überprüft haben.

Node.js behandelt zirkuläre Abhängigkeiten auf einfache Weise. Während des Ladens des Moduls wird das Exportobjekt schrittweise aufgebaut und den Exporten werden Werte zugewiesen. Wenn wir ein Modul importieren, bevor es vollständig geladen ist, erhalten wir nur einige der Eigenschaften des exportierten Objekts.

2.7. .json und .node

In Node.js können wir „require“ nicht nur zum Verweisen auf JavaScript-Dateien verwenden, sondern auch zum Verweisen auf JSON- oder C++-Plug-ins (.json- und .node-Dateien). Wir müssen nicht einmal die entsprechende Dateierweiterung explizit deklarieren.

Sie können die von require unterstützten Dateitypen auch in der Befehlszeile sehen:

~ % Knoten

> erforderliche.Erweiterungen

[Objekt: Null-Prototyp] {

'.js': [Funktion (anonym)],

'.json': [Funktion (anonym)],

'.node': [Funktion (anonym)]

}

Wenn wir require verwenden, um auf ein Modul zu verweisen, prüft Node.js zunächst, ob eine JS-Datei vorhanden ist. Wenn keine gefunden wird, wird eine Übereinstimmung mit der JSON-Datei gesucht. Wenn diese immer noch nicht gefunden wird, wird schließlich versucht, eine Übereinstimmung mit der Node-Datei zu finden. Um Verwirrungen und unklare Referenzabsichten zu vermeiden, können Sie im Allgemeinen der Regel folgen, beim Verweisen auf .json- oder .node-Dateien das Suffix explizit anzugeben und beim Verweisen auf .js-Dateien das Suffix wegzulassen (optional oder beides).

.json-Datei:

Es ist sehr üblich, auf JSON-Dateien zu verweisen. Beispielsweise lässt sich die statische Konfiguration in einigen Projekten einfacher verwalten, indem JSON-Dateien zum Speichern verwendet werden. Beispiel:

{
  "Host": "lokaler Host",
  "Port": 8080
}

Es ist ganz einfach, darauf zu verweisen oder es zu verwenden:

const { Host, Port } = erforderlich('./config');
console.log(`Der Server wird unter http://${host}:${port} ausgeführt`)

Die Ausgabe lautet wie folgt:

Der Server wird unter http://localhost:8080 ausgeführt.

.node-Datei:

Die .node-Datei wird aus einer C++-Datei konvertiert. Die offizielle Website bietet ein einfaches, in C++ implementiertes Hello-Plugin, das eine hello()-Methode verfügbar macht und den String „world“ ausgibt. Bei Bedarf können Sie dem Link folgen, um mehr zu erfahren und Experimente durchzuführen.

Wir können node-gyp verwenden, um .cc-Dateien zu kompilieren und in .node-Dateien zu erstellen. Der Vorgang ist auch sehr einfach. Wir müssen nur eine binding.gyp-Datei konfigurieren. Ich werde hier nicht ins Detail gehen. Sie müssen nur wissen, dass Sie nach dem Generieren der .node-Datei normal auf die Datei verweisen und die darin enthaltenen Methoden verwenden können.

Nachdem Sie beispielsweise hello() in die Datei addon.node konvertiert haben, können Sie darauf verweisen und es verwenden:

const addon = erfordern('./addon');
Konsole.log(addon.hallo());

2.8 Umhüllung

Tatsächlich haben wir im obigen Inhalt die ersten beiden Schritte zum Referenzieren eines Moduls in Node.js erläutert: Auflösen und Laden, die jeweils die Probleme des Modulpfads und des Ladens lösen. Schauen wir uns als Nächstes an, was Wrapping bewirkt.

Wrapping bedeutet Verpacken, und das Objekt des Verpackens ist der gesamte Code, den wir im Modul geschrieben haben. Das heißt, wenn wir auf ein Modul verweisen, durchlaufen wir tatsächlich eine Schicht „transparenter“ Verpackung.

Um diesen Verpackungsprozess zu verstehen, müssen Sie zunächst den Unterschied zwischen Exporten und Modulexporten verstehen.

Exporte ist ein Verweis auf module.exports. Wir können Exporte in Modulen verwenden, um Eigenschaften zu exportieren, aber wir können es nicht direkt ersetzen. Zum Beispiel:

exports.id = 42; // ok, jetzt verweist exports auf module.exports, was dem Ändern von module.exports entspricht.
exports = { id: 42 }; // Nutzlos, verweist nur auf das Objekt { id: 42 }, keine tatsächlichen Änderungen an module.exports.
module.exports = { id: 42 }; // OK, führe module.exports direkt aus.

Sie fragen sich vielleicht, warum das Exportobjekt für jedes Modul ein globales Objekt zu sein scheint, aber erkennen kann, aus welchem ​​Modul das exportierte Objekt stammt. Wie wird das gemacht?

Bevor wir den Wrapping-Prozess verstehen, schauen wir uns ein kleines Beispiel an:

// In a.js
Var-Wert = "global"
  
// In b.js
console.log(Wert) // Ausgabe: global
  
// In c.js
console.log(Wert) // Ausgabe: global
  
// In index.html
...
<script src="a.js"></script>
<script src="b.js"></script>
<script src="c.js"></script>

Wenn wir einen Wert im a.js-Skript definieren, ist dieser Wert global sichtbar und die nachfolgend eingeführten b.js und c.js können auf den Wert zugreifen. Dies ist jedoch in Node.js-Modulen nicht der Fall. In einem Modul definierte Variablen haben einen privaten Gültigkeitsbereich und können in anderen Modulen nicht direkt aufgerufen werden. Wie kommt dieser private Spielraum zustande?

Die Antwort ist einfach. Vor dem Kompilieren des Moduls verpackt Node.js den Inhalt des Moduls in eine Funktion und implementiert den privaten Bereich über den Funktionsbereich.

Mit require('module').wrapper können Sie die Wrapper-Eigenschaften ausdrucken:

~ $ Knoten

> erfordern('Modul').wrapper

[ '(Funktion (Exporte, erfordern, Modul, __Dateiname, __Verzeichnisname) { ',

'\N});' ]

>

Node.js führt keinen Code in der Datei direkt aus, sondern führt den Code über diese gewrappte Funktion aus, die jedem unserer Module einen privaten Bereich gibt und sich nicht gegenseitig beeinflusst.

Diese Wrapper-Funktion hat fünf Parameter: Exports, Require, Module, __Filename, __Dirname. Wir können auf diese Parameter direkt über den Argumentparameter zugreifen und sie ausdrucken:

./learn-node $ echo "console.log(argumente)" > index.js

~/learn-node $ Knotenindex.js

{ '0': {},

'1':

{ [Funktion: erfordern]

lösen: [Funktion: lösen],

hauptsächlich:

Modul {

Ausweis: '.',

Exporte: {},

übergeordnetes Element: null,

Dateiname: '/Users/samer/index.js',

geladen: falsch,

Kinder: [],

Pfade: [Objekt] },

Erweiterungen: { ... },

Cache: { '/Users/samer/index.js': [Objekt] } },

'2':

Modul {

Ausweis: '.',

Exporte: {},

übergeordnetes Element: null,

Dateiname: '/Users/samer/index.js',

geladen: falsch,

Kinder: [],

Pfade: [ ... ] },

'3': '/Benutzer/samer/index.js',

'4': '/Benutzer/samer' }

Werfen wir einen kurzen Blick auf diese Parameter. Der erste Parameter exports ist zunächst leer (nicht zugewiesen). Der zweite und dritte Parameter require und module sind Instanzen, die sich auf das von uns referenzierte Modul beziehen. Sie sind nicht global. Der vierte und fünfte Parameter __filename und __dirname stellen jeweils den Dateipfad und das Verzeichnis dar.

Die gesamte gewrappte Funktion macht ungefähr dasselbe:

unction (erfordern, Modul, __Dateiname, __Verzeichnisname) {
  lass exports = modul.exports;
    
  // Ihr Code ...
    
  Rückgabemodul.Exporte;
}

Kurz gesagt dient das Wrapping dazu, unseren Modulbereich zu privatisieren und Variablen oder Methoden mit module.exports als Rückgabewert zur Verwendung verfügbar zu machen.

2.9 Zwischenspeicher

Caching ist leicht zu verstehen. Schauen wir es uns anhand eines Beispiels an:

echo 'console.log(`etwas protokollieren.`)' > index.js

// Im Knoten repl

> erfordern('./index.js')

etwas protokollieren.

{}

> erfordern('./index.js')

{}

>

Wie Sie sehen, wird das gleiche Modul zweimal referenziert, die Informationen werden jedoch nur einmal gedruckt. Dies liegt daran, dass der Cache für die zweite Referenz verwendet wird und das Modul nicht neu geladen werden muss.

Drucken Sie require.cache, um die aktuellen Cache-Informationen anzuzeigen:

> require.cache

[Objekt: Null-Prototyp] {

'/Benutzer/samer/index.js': Modul {

ID: "/Benutzer/samer/index.js",

Pfad: '/Users/samer/',

Exporte: {},

übergeordnetes Element: Modul {

ID: '<repl>',

Weg: '.',

Exporte: {},

Elternteil: undefiniert,

Dateiname: null,

geladen: falsch,

Kinder: [Array],

Pfade: [Array]

},

Dateiname: '/Users/samer/index.js',

geladen: wahr,

Kinder: [],

Pfade: [

„/Benutzer/samer/learn-node/repl/node_modules“,

'/Benutzer/samer/learn-node/node_modules',

'/Benutzer/samer/node_modules',

'/Benutzer/node_modules',

'/Knotenmodule',

'/Benutzer/samer/.node_modules',

„/Benutzer/samer/.node_libraries“,

„/usr/local/Cellar/node/7.7.1/lib/node“

]

}

}

Sie können sehen, dass sich die gerade referenzierte Datei index.js im Cache befindet, sodass das Modul nicht neu geladen wird. Natürlich können wir den Cache-Inhalt auch löschen, indem wir require.cache löschen, um das Neuladen zu erreichen, aber ich werde es hier nicht demonstrieren.

Abschluss

In diesem Artikel werden einige grundlegende Prinzipien und der gesunde Menschenverstand beschrieben, die Sie bei der Verwendung der Node.js-Modularisierung kennen müssen. Er soll allen zu einem klareren Verständnis der Node.js-Modularisierung verhelfen. In diesem Artikel werden jedoch keine tieferen Details erläutert, wie etwa die Verarbeitungslogik innerhalb der Wrapper-Funktion, das Problem des synchronen Ladens von CommonJS, der Unterschied zu ES-Modulen usw. Sie können weitere dieser nicht erwähnten Inhalte außerhalb dieses Artikels erkunden.

Oben finden Sie eine ausführliche Erläuterung der NodeJS-Modularisierung. Weitere Informationen zur NodeJS-Modularisierung finden Sie in den anderen verwandten Artikeln auf 123WORDPRESS.COM!

Das könnte Sie auch interessieren:
  • Detaillierte Erklärung der Modularisierung in Node.js
  • Modularität in Node.js, npm-Paketmanager erklärt
  • Pufferobjekt in Node.js und wie man es erstellt
  • Detaillierte Erläuterung der Verwendung des Pufferobjekts im binären Operationsmodul von node.JS
  • Detaillierte Erläuterung des binären Pufferobjekts von nodeJS
  • Detaillierte Erläuterung des modularen Mechanismus und des Pufferobjekts von Node.js

<<:  Erläuterung zur Verwendung von „if“-Beurteilungsbedingungen in Summen- und Zählfunktionen bei der Verwendung von SQL-Anweisungen zum Sammeln von Daten

>>:  Die Fallstricke bei der Bereitstellung von Angular-Projekten in Nginx

Artikel empfehlen

Centos8 erstellt NFS basierend auf KDC-Verschlüsselung

Inhaltsverzeichnis Konfiguration NFS-Server (nfs....

Vue Element UI-Komponente für benutzerdefinierte Beschreibungsliste

In diesem Artikelbeispiel wird der spezifische Co...

Detaillierte Erklärung des Nginx Reverse-Proxy-Beispiels

1. Reverse-Proxy-Beispiel 1 1. Erzielen Sie den E...

Grafisches Tutorial zur MySQL 5.7-Konfiguration ohne Installation

Mysql ist eine beliebte und einfach zu bedienende...

Einige Einstellungen von Div bezüglich Rahmen und Transparenz

rahmen: Stil = „Rahmenstil: durchgezogen; Rahmenbr...

Interpretation des Moduls zum Lastenausgleich mit nginx

Inhaltsverzeichnis Zwei Module zur Verwendung von...

Mehrere gängige Methoden zum Senden von Anfragen mit Axios in React

Inhaltsverzeichnis Installieren und Einführen von...

Transplantieren des Befehls mkfs.vfat in Busybox unter Linux

Um die Lebensdauer der Festplatte zum Speichern v...

5 einfache Möglichkeiten, Speicherplatz auf Ubuntu freizugeben

Vorwort Die meisten Benutzer führen diesen Vorgan...

Reines HTML und CSS, um den JD-Karusselleffekt zu erzielen

Das JD-Karussell wurde mit reinem HTML und CSS im...

MySQL partitioniert vorhandene Tabellen in der Datentabelle

Inhaltsverzeichnis So funktioniert es Betriebsabl...