Alles, was Sie über 'module' & 'require' in Node.js wissen sollten

Module

Node.js behandelt jede JavaScript-Datei als separates Modul.

Wenn Sie beispielsweise eine Datei mit Code haben und diese Datei benannt ist xyz.js, wird diese Datei in Node als Modul behandelt , und Sie können sagen, dass Sie ein Modul mit dem Namen erstellt haben xyz.

Nehmen wir ein Beispiel, um dies besser zu verstehen.

Sie haben eine Datei mit dem Namen, circle.jsdie aus der Logik zur Berechnung der Fläche und des Umfangs eines Kreises mit einem bestimmten Radius besteht, wie unten angegeben:

circle.js

Sie können circle.jsfile ein Modul mit dem Namen aufrufen circle.

Sie fragen sich vielleicht, warum mehrere Module erforderlich sind? Sie hätten einfach den gesamten Code in einem einzigen Modul schreiben können. Nun, es ist sehr wichtig, modularen Code zu schreiben. Mit modular meine ich zu sagen, dass Ihr Code unabhängig und lose gekoppelt sein sollte. Stellen Sie sich vor, es gibt eine große Anwendung und Sie haben Ihren gesamten Code an nur einer Stelle geschrieben, nur in einer Datei. Zu chaotisch, oder?

Wie läuft der in einem Modul geschriebene Code ab?

Vor dem Ausführen des in ein Modul geschriebenen Codes nimmt Node den gesamten Code und schließt ihn in einen Funktionsumbruch ein. Die Syntax dieses Funktions-Wrappers lautet:

Der Funktions-Wrapper für das circleModul sieht wie folgt aus:

Sie können sehen, dass es auf der Stammebene einen Funktionsumbruch gibt, der den gesamten im circleModul geschriebenen Code umfasst .

Der gesamte in ein Modul geschriebene Code ist für das Modul privat, sofern nicht ausdrücklich anders angegeben (exportiert).

Dies ist der wichtigste Vorteil von Modulen in Node.js. Auch wenn Sie eine globale Variable in einem Modul definieren var, letoder constSchlüsselwörter, werden die Variablen lokal auf dem Modul scoped statt global scoped werden. Dies liegt daran, dass jedes Modul einen eigenen Funktionsumbruch hat und der in einer Funktion geschriebene Code für diese Funktion lokal ist und außerhalb dieser Funktion nicht aufgerufen werden kann.

Stellen Sie sich vor , dass es zwei Module - A und B . Der im Modul A geschriebene Code ist im Funktionsumbruch enthalten, der dem Modul A entspricht . Ähnliches passiert mit dem Code, der in Modul B geschrieben ist . Da der Code für beide Module in verschiedenen Funktionen enthalten ist, können diese Funktionen nicht auf den Code voneinander zugreifen. (Denken Sie daran, dass jede Funktion in JavaScript ihren eigenen lokalen Bereich hat?) Dies ist der Grund, warum Modul A nicht auf den in Modul B geschriebenen Code zugreifen kann und umgekehrt.

Die fünf Parameter - exports, require, module, __filename, __dirnamesind in jedem Modul in Knoten zur Verfügung. Obwohl diese Parameter für den Code innerhalb eines Moduls global sind, sind sie für das Modul lokal (aufgrund des oben erläuterten Funktions-Wrappers). Diese Parameter liefern wertvolle Informationen zu einem Modul.

Lassen Sie uns das circleModul, das Sie sich zuvor angesehen haben, erneut betrachten. In diesem Modul sind drei Konstrukte definiert - eine konstante Variable PI, eine benannte Funktion calculateAreaund eine andere benannte Funktion calculateCircumference. Ein wichtiger Punkt, den Sie berücksichtigen sollten, ist, dass alle diese Konstrukte circlestandardmäßig für das Modul privat sind . Dies bedeutet, dass Sie diese Konstrukte in keinem anderen Modul verwenden können, sofern dies nicht ausdrücklich angegeben ist.

Die Frage, die sich jetzt stellt, ist, wie Sie etwas in einem Modul angeben, das von einem anderen Modul verwendet werden kann. In diesem Fall sind die module& require-Parameter des Funktionsumschlags hilfreich. Lassen Sie uns diese beiden Parameter in diesem Artikel diskutieren.

module

Der moduleParameter (eher ein Schlüsselwort in einem Modul in Node) bezieht sich auf das Objekt, das das aktuelle Modul darstellt . exportsist ein Schlüssel des moduleObjekts, dessen entsprechender Wert ein Objekt ist. Der Standardwert des module.exportsObjekts ist {}(leeres Objekt). Sie können dies überprüfen, indem Sie den Wert des moduleSchlüsselworts in einem beliebigen Modul protokollieren . Lassen Sie uns überprüfen, welchen Wert der moduleParameter im circleModul hat.

circle.js

Beachten Sie, dass console.log(module);am Ende des Codes in der oben angegebenen Datei eine Anweisung steht. Wenn Sie die Ausgabe sehen, wird das moduleObjekt protokolliert , dessen Schlüssel benannt ist exportsund dessen Wert diesem Schlüssel entspricht {}(ein leeres Objekt).

Was macht das module.exportsObjekt nun? Nun, es wird zum Definieren von Dingen verwendet, die von einem Modul exportiert werden können. Was auch immer aus einem Modul exportiert wird, kann wiederum anderen Modulen zur Verfügung gestellt werden. Etwas zu exportieren ist ganz einfach. Sie müssen es nur dem module.exportsObjekt hinzufügen . Es gibt drei Möglichkeiten, dem module.exportszu exportierenden Objekt etwas hinzuzufügen . Lassen Sie uns diese Methoden einzeln diskutieren.

Methode 1:

(Konstrukte definieren und dann mehrere module.exportsAnweisungen verwenden, um Eigenschaften hinzuzufügen)

In der ersten Methode definieren Sie zuerst die Konstrukte und verwenden dann mehrere module.exports- Anweisungen, wobei jede Anweisung verwendet wird, um etwas aus einem Modul zu exportieren. Schauen wir uns diese Methode in Aktion an und sehen, wie Sie die beiden im circleModul definierten Funktionen exportieren können .

circle.js

Wie ich Ihnen bereits sagte, modulehandelt es sich um ein Objekt mit dem Namen key, exportsund dieses key ( module.exports) besteht wiederum aus einem anderen Objekt. Wenn Sie nun den oben angegebenen Code bemerken, fügen Sie dem module.exportsObjekt lediglich neue Eigenschaften (Schlüssel-Wert-Paare) hinzu .

Die erste Eigenschaft hat den Schlüssel calculateArea(definiert in Zeile 19)und der Wert, der auf der rechten Seite des Zuweisungsoperators geschrieben ist, ist die mit dem Namen definierte Funktion calculateArea(in Zeile 9).

Die zweite Eigenschaft (definiert in Zeile 20) hat den Schlüssel calculateCircumferenceund der Wert ist die mit dem Namen definierte Funktion calculateCircumference(in Zeile 16).

Daher haben Sie dem module.exportsObjekt zwei Eigenschaften (Schlüssel-Wert-Paare) zugewiesen .

Vergessen wir auch nicht, dass Sie hier die Punktnotation verwendet haben. Alternativ können Sie die Klammernotation verwenden, um dem module.exportsObjekt die Eigenschaften zuzuweisen und die Funktionen hinzuzufügen - calculateAreaund calculateCircumferenceindem Sie die Tasten nach der Klammernotation angeben. Daher können Sie die folgenden zwei Zeilen schreiben, um dem module.exportsObjekt Eigenschaften in Klammernotation hinzuzufügen, während Sie die letzten beiden Zeilen (in Punktnotation) im oben angegebenen Code ersetzen:

// exporting stuff by adding to module.exports object using the bracket notation
module.exports['calculateArea'] = calculateArea;module.exports['calculateCircumference'] = calculateCircumference; 

Versuchen wir nun, den Wert des module.exportsObjekts nach dem Hinzufügen der Eigenschaften zu protokollieren . Beachten Sie, dass die folgende Anweisung am Ende des Codes in der unten angegebenen Datei hinzugefügt wird:

// logging the contents of module.exports object after adding properties to it
console.log(module.exports);

circle.js

Lassen Sie uns die Ausgabe dieses Codes überprüfen und feststellen, ob alles einwandfrei funktioniert. Speichern Sie dazu Ihren Code und führen Sie den folgenden Befehl in Ihrem Terminal aus :

node circle

Ausgabe:

{ calculateArea: [Function: calculateArea], calculateCircumference: [Function: calculateCircumference] }

Die Konstrukte - calculateAreaund calculateCircumferencedas zum module.exportsObjekt hinzugefügte Objekt werden protokolliert. So haben Sie die beiden Eigenschaften erfolgreich in das module.exportsObjekt eingefügt, damit die Funktionen - calculateAreaund calculateCircumferenceaus dem circleModul in ein anderes Modul exportiert werden können.

Bei dieser Methode haben Sie zuerst alle Konstrukte definiert und dann mehrere module.exports- Anweisungen verwendet, wobei jede Anweisung zum Hinzufügen einer Eigenschaft zum module.exportsObjekt verwendet wird.

Methode 2:

(Konstrukte definieren und dann mit einer einzigen module.exportsAnweisung Eigenschaften hinzufügen)

Eine andere Möglichkeit besteht darin, zuerst alle Konstrukte zu definieren (wie in der vorherigen Methode), aber alle mit einer einzigen module.exportsAnweisung zu exportieren. Diese Methode ähnelt der Syntax der Objektliteralnotation, bei der Sie einem Objekt alle Eigenschaften gleichzeitig hinzufügen.

Hier haben Sie die Objektliteralnotation verwendet und beide Funktionen - calculateArea und calculateCircumference(auf einmal) zum module.exportsObjekt hinzugefügt, indem Sie eine einzelne Anweisung module.exports geschrieben haben .

Wenn Sie die Ausgabe dieses Codes überprüfen, erhalten Sie das gleiche Ergebnis wie zuvor bei Verwendung von Methode 1.

Methode 3:

(Hinzufügen von Eigenschaften zum module.exportsObjekt beim Definieren von Konstrukten)

Bei dieser Methode können Sie die Konstrukte zum module.exportsObjekt hinzufügen, während Sie sie definieren. Mal sehen, wie diese Methode in unser circleModul übernommen werden kann.

In dem oben angegebenen Code können Sie sehen, dass die Funktionen im Modul dem module.exportsObjekt hinzugefügt werden, wenn sie definiert werden. Schauen wir uns an, wie das funktioniert. Sie fügen calculateAreadem module.exportsObjekt einen Schlüssel hinzu , und der diesem Schlüssel entsprechende Wert ist die Funktionsdefinition.

Beachten Sie, dass die Funktion keinen Namen mehr hat und eine anonyme Funktion ist, die nur als Wert für einen Schlüssel eines Objekts behandelt wird. Daher kann im circleModul nicht auf diese Funktion verwiesen werden, und Sie können diese Funktion in diesem Modul nicht aufrufen, indem Sie die folgende Anweisung schreiben:

calculateArea(8);

Wenn Sie versuchen, die obige Anweisung auszuführen, erhalten Sie eine ReferenceErrorAngabe calculateArea is not defined.

Nachdem Sie nun gelernt haben, wie Sie festlegen können, was aus einem Modul exportiert werden soll, wie kann das andere Modul das exportierte Material verwenden? Sie müssen das Modul in ein anderes Modul importieren, um das exportierte Material aus dem ersteren in dem letzteren verwenden zu können. In diesem Fall müssen wir einen anderen Parameter mit dem Namen diskutieren require.

benötigen

requireDas Schlüsselwort bezieht sich auf eine Funktion, mit der alle Konstrukte importiert werden, die mit dem module.exportsObjekt aus einem anderen Modul exportiert wurden . Wenn Sie ein Modul x haben, in das Sie einige Konstrukte mit dem module.exportsObjekt exportieren und diese exportierten Konstrukte in Modul y importieren möchten , müssen Sie das Modul x im Modul y mithilfe der requireFunktion benötigen . Der von der requireFunktion im Modul y zurückgegebene Wert entspricht dem module.exportsObjekt im Modul x .

Lassen Sie uns dies anhand des Beispiels verstehen, das wir zuvor besprochen haben. Sie haben bereits das circleModul, aus dem Sie die Funktionen calculateAreaund exportieren calculateCircumference. Lassen Sie uns nun sehen, wie Sie die requireFunktion verwenden können, um das exportierte Material in ein anderes Modul zu importieren.

Erstellen wir zunächst eine neue Datei, in der Sie den aus dem circleModul exportierten Code verwenden . app.jsNennen wir diese Datei und Sie können sie als appModul bezeichnen.

Ziel ist es, den appgesamten aus dem circleModul exportierten Code in das Modul zu importieren . Wie können Sie also Ihren in ein Modul geschriebenen Code in ein anderes Modul aufnehmen?

Betrachten Sie die Syntax der requireunten angegebenen Funktion:

const variableToHoldExportedStuff = require('idOrPathOfModule');

Die requireFunktion nimmt ein Argument auf, das eine ID oder ein Pfad sein kann. Die ID bezieht sich auf die ID (oder den Namen) des benötigten Moduls. Sie sollten ID als Argument angeben, wenn Sie die vom Node Package Manager bereitgestellten Module oder Kernmodule von Drittanbietern verwenden. Wenn Sie dagegen benutzerdefinierte Module definiert haben, sollten Sie den Pfad des Moduls als Argument angeben. Weitere Informationen zur erforderlichen Funktion finden Sie unter diesem Link.

Da Sie bereits ein benutzerdefiniertes Modul mit dem Namen definiert haben circle, geben Sie den Pfad als Argument für die requireFunktion an.

app.js.

Wenn Sie dies deutlich bemerken, bedeutet der Punkt am Anfang des Pfads, dass es sich um einen relativen Pfad handelt und dass die Module appund circleauf demselben Pfad gespeichert sind.

Melden wir uns an der Konsole der circleVariablen an, die das von der requireFunktion zurückgegebene Ergebnis enthält . Mal sehen, was in dieser Variablen enthalten ist.

app.js.

Überprüfen Sie die Ausgabe, indem Sie Ihren gesamten Code speichern und den folgenden Befehl in Ihrem Terminal ausführen (letzterer ist nicht erforderlich, wenn Sie ein nodemonPaket installiert haben):

node app

Ausgabe:

{ calculateArea: [Function: calculateArea],calculateCircumference: [Function: calculateCircumference] }

Wie Sie sehen können, gibt die requireFunktion ein Objekt zurück, dessen Schlüssel die Namen der Variablen / Funktionen sind, die aus dem erforderlichen Modul ( circle) exportiert wurden . Kurz gesagt, die requireFunktion gibt das module.exportsObjekt zurück.

Greifen wir nun auf die aus dem circleModul importierten Funktionen zu .

app.js.

Ausgabe:

Area = 200.96, Circumference = 50.24

Was wird Ihrer Meinung nach passieren, wenn ich versuche, auf die PIim circleModul innerhalb des appModuls definierte Variable zuzugreifen ?

app.js.

Ausgabe:

Area = 200.96, Circumference = 50.24pi = undefined

Können Sie herausfinden, warum das so piist undefined? Dies liegt daran, dass die Variable PInicht aus dem circleModul exportiert wird. Erinnern Sie sich an den Punkt, an dem ich Ihnen gesagt habe, dass Sie nicht auf den in einem Modul in einem anderen Modul geschriebenen Code zugreifen können, da der gesamte in einem Modul geschriebene Code für ihn privat ist, sofern er nicht exportiert wird? Hier versuchen Sie, auf etwas zuzugreifen, das nicht aus dem circleModul exportiert wurde und für dieses privat ist.

Sie fragen sich vielleicht, warum Sie keine bekommen haben ReferenceError. Dies liegt daran, dass Sie versuchen, auf einen Schlüssel zuzugreifen, der PIinnerhalb des module.exportsvon der requireFunktion zurückgegebenen Objekts benannt ist . Sie wissen auch, dass der genannte Schlüssel PIim module.exportsObjekt nicht vorhanden ist .

Beachten Sie, dass Sie beim Versuch, auf einen nicht vorhandenen Schlüssel in einem Objekt zuzugreifen, das Ergebnis als erhalten undefined. Dies ist der Grund, warum Sie PIals undefinedstatt bekommen ReferenceError.

Exportieren wir nun die Variable PIaus dem circleModul und prüfen, ob sich die Antwort ändert.

circle.js

Beachten Sie, dass Sie hier nicht den Namen der Variablen PIals Schlüssel für die dem Objekt hinzugefügte Eigenschaft verwenden module.exports. Sie verwenden stattdessen einen anderen Namen lifeOfPi.

Dies ist eine interessante Sache zu beachten. Wenn Sie ein Codierungskonstrukt exportieren, können Sie dem Schlüssel einen beliebigen Namen geben, wenn Sie dem module.exportsObjekt eine Eigenschaft hinzufügen . Es ist nicht zwingend erforderlich, denselben Namen wie den Namen zu verwenden, den Sie beim Definieren des Konstrukts verwendet haben. Dies liegt daran, dass Sie einen beliebigen gültigen Bezeichner als Schlüssel in einem JavaScript-Objekt verwenden können. Auf der linken Seite des Zuweisungsoperators können Sie also einen beliebigen gültigen Bezeichner verwenden. Auf der rechten Seite des Zuweisungsoperators müssen Sie jedoch einen Wert angeben, der im Bereich des aktuellen Moduls (wie Sie) als Konstrukt definiert ist habe die Variablen und Funktionen im Modul 'Kreis' definiert).

Ein wichtiger Punkt ist, dass Sie beim Importieren von etwas aus einem anderen Modul im aktuellen Modul denselben Schlüssel verwenden müssen, den Sie beim Exportieren verwendet haben.

app.js.

Da Sie den Schlüssel verwendet haben lifeOfPi, müssen Sie denselben Schlüssel verwenden, um auf die PIim circleModul definierte Variable zuzugreifen , wie im oben angegebenen Code.

Ausgabe:

Area = 200.96, Circumference = 50.24pi = 3.14

Was wird Ihrer Meinung nach passieren, wenn Sie den Namen der Variablen anstelle des beim Export verwendeten Schlüssels verwenden? Kurz gesagt, versuchen wir, auf PI(Name der Variablen) anstatt auf lifeOfPi(beim Export verwendeten Schlüssel ) zuzugreifen PI.

app.js.

Ausgabe:

Area = 200.96, Circumference = 50.24pi = undefined

Dies geschieht, weil das module.exportsObjekt die Variable PInicht mehr kennt . Es kennt nur die hinzugefügten Schlüssel. Da der zum Exportieren der Variablen verwendete Schlüssel PIlautet lifeOfPi, kann der letztere nur für den Zugriff auf den ersteren verwendet werden.

TL; DR

  • Jede Datei in Node.js wird als Modul bezeichnet .
  • Vor dem Ausführen des in einem Modul geschriebenen Codes nimmt Node.js den gesamten im Modul geschriebenen Code und konvertiert ihn in einen Funktionswrapper mit der folgenden Syntax:
(function(exports, require, module, __filename, __dirname) { // entire module code lives in here});
  • Der Funktions-Wrapper stellt sicher, dass der gesamte in einem Modul geschriebene Code privat ist, sofern nicht ausdrücklich anders angegeben (exportiert). Die Parameter exports, require, module, __filename, und __dirnamewirkt als die Variablen global für den gesamten Code in einem Modul. Da jedes Modul einen eigenen Funktions-Wrapper hat, wird der in einem Funktions-Wrapper geschriebene Code für diesen Funktions-Wrapper (Lesemodul) lokal und ist in einem anderen Funktions-Wrapper (Lesemodul) nicht zugänglich.
  • moduleDas Schlüsselwort bezieht sich auf das Objekt, das das aktuelle Modul darstellt. Das moduleObjekt hat einen Schlüssel mit dem Namen exports. module.exportsist ein weiteres Objekt, mit dem definiert wird, was von einem Modul exportiert und anderen Modulen zur Verfügung gestellt werden kann. Kurz gesagt, wenn ein Modul etwas exportieren möchte, sollte es dem module.exportsObjekt hinzugefügt werden.
  • Der Standardwert des module.exportsObjekts ist {}.
  • Es gibt drei Methoden, mit denen Sie etwas aus einem Modul exportieren oder dem module.exportsObjekt etwas hinzufügen können :

    1. Definieren Sie zuerst alle Konstrukte und verwenden Sie dann mehrere module.exportsAnweisungen, wobei jede Anweisung zum Exportieren eines Konstrukts verwendet wird.

    2. Definieren Sie zuerst alle Konstrukte und module.exportsexportieren Sie dann mit einer einzigen Anweisung alle Konstrukte auf einmal nach der Objektliteralnotation.

    3. Fügen Sie dem module.exportsObjekt Konstrukte hinzu, während Sie sie definieren.

  • requireDas Schlüsselwort bezieht sich auf eine Funktion, mit der alle Variablen und Funktionen importiert werden, die mit dem module.exportsObjekt aus einem anderen Modul exportiert wurden . Kurz gesagt, wenn eine Datei etwas importieren möchte, muss sie es mit der folgenden Syntax deklarieren:
require('idOrPathOfModule');
  • Während Sie etwas aus einem Modul exportieren, können Sie einen beliebigen gültigen Bezeichner verwenden. Es ist nicht zwingend erforderlich, dass Sie den genauen Namen der Variablen / Funktion als Schlüssel für die dem Objekt hinzugefügte Eigenschaft module.exportsangeben. Stellen Sie einfach sicher, dass Sie denselben Schlüssel verwenden, um auf etwas zuzugreifen, das Sie beim Exportieren verwendet haben.