Module: CommonJS-Module
[Stabil: 2 - Stabil]
Stabil: 2 Stabilität: 2 - Stabil
CommonJS-Module sind die ursprüngliche Methode zur Paketierung von JavaScript-Code für Node.js. Node.js unterstützt auch den ECMAScript-Module-Standard, der von Browsern und anderen JavaScript-Laufzeitumgebungen verwendet wird.
In Node.js wird jede Datei als separates Modul behandelt. Betrachten Sie beispielsweise eine Datei namens foo.js
:
const circle = require('./circle.js');
console.log(`Die Fläche eines Kreises mit Radius 4 beträgt ${circle.area(4)}`);
In der ersten Zeile lädt foo.js
das Modul circle.js
, das sich im selben Verzeichnis wie foo.js
befindet.
Hier ist der Inhalt von circle.js
:
const { PI } = Math;
exports.area = (r) => PI * r ** 2;
exports.circumference = (r) => 2 * PI * r;
Das Modul circle.js
hat die Funktionen area()
und circumference()
exportiert. Funktionen und Objekte werden dem Stamm eines Moduls hinzugefügt, indem zusätzliche Eigenschaften für das spezielle exports
-Objekt angegeben werden.
Variablen, die lokal zum Modul sind, sind privat, da das Modul von Node.js in eine Funktion eingeschlossen wird (siehe Modulumhüllung). In diesem Beispiel ist die Variable PI
für circle.js
privat.
Der Eigenschaft module.exports
kann ein neuer Wert zugewiesen werden (z. B. eine Funktion oder ein Objekt).
Im folgenden Code verwendet bar.js
das square
-Modul, das eine Square-Klasse exportiert:
const Square = require('./square.js');
const mySquare = new Square(2);
console.log(`Die Fläche von mySquare beträgt ${mySquare.area()}`);
Das square
-Modul ist in square.js
definiert:
// Die Zuweisung zu exports ändert das Modul nicht, es muss module.exports verwendet werden
module.exports = class Square {
constructor(width) {
this.width = width;
}
area() {
return this.width ** 2;
}
};
Das CommonJS-Modulsystem ist im module
-Kernmodul implementiert.
Aktivierung
Node.js verfügt über zwei Modulsysteme: CommonJS-Module und ECMAScript-Module.
Standardmäßig behandelt Node.js Folgendes als CommonJS-Module:
- Dateien mit der Erweiterung
.cjs
; - Dateien mit der Erweiterung
.js
, wenn die nächstgelegene übergeordnetepackage.json
-Datei ein Feld der obersten Ebene"type"
mit dem Wert"commonjs"
enthält. - Dateien mit einer
.js
-Erweiterung oder ohne Erweiterung, wenn die nächstgelegene übergeordnetepackage.json
-Datei kein Feld der obersten Ebene"type"
enthält oder keinepackage.json
in einem übergeordneten Ordner vorhanden ist; es sei denn, die Datei enthält eine Syntax, die einen Fehler verursacht, wenn sie nicht als ES-Modul ausgewertet wird. Paketautoren sollten das Feld"type"
auch in Paketen einfügen, in denen alle Quellen CommonJS sind. Die explizite Angabe destype
des Pakets erleichtert Build-Tools und Ladeprogrammen die Bestimmung, wie die Dateien im Paket interpretiert werden sollen. - Dateien mit einer Erweiterung, die nicht
.mjs
,.cjs
,.json
,.node
oder.js
ist (wenn die nächstgelegene übergeordnetepackage.json
-Datei ein Feld der obersten Ebene"type"
mit dem Wert"module"
enthält, werden diese Dateien nur dann als CommonJS-Module erkannt, wenn sie überrequire()
eingebunden werden, nicht, wenn sie als Befehlszeileneinstiegspunkt des Programms verwendet werden).
Weitere Informationen finden Sie unter Bestimmung des Modulsystems.
Der Aufruf von require()
verwendet immer den CommonJS-Modul-Loader. Der Aufruf von import()
verwendet immer den ECMAScript-Modul-Loader.
Zugriff auf das Hauptmodul
Wenn eine Datei direkt von Node.js aus ausgeführt wird, wird require.main
auf ihr module
gesetzt. Das bedeutet, dass es möglich ist zu bestimmen, ob eine Datei direkt ausgeführt wurde, indem man require.main === module
testet.
Für eine Datei foo.js
ist dies true
, wenn sie über node foo.js
ausgeführt wird, aber false
, wenn sie von require('./foo')
ausgeführt wird.
Wenn der Einstiegspunkt kein CommonJS-Modul ist, ist require.main
undefined
, und das Hauptmodul ist nicht erreichbar.
Tipps für Paketmanager
Die Semantik der Node.js-Funktion require()
wurde so allgemein konzipiert, dass sie vernünftige Verzeichnisstrukturen unterstützt. Paketmanager-Programme wie dpkg
, rpm
und npm
werden hoffentlich in der Lage sein, native Pakete aus Node.js-Modulen ohne Änderungen zu erstellen.
Im Folgenden geben wir eine vorgeschlagene Verzeichnisstruktur, die funktionieren könnte:
Nehmen wir an, wir möchten den Ordner unter /usr/lib/node/\<some-package\>/\<some-version\>
den Inhalt einer bestimmten Version eines Pakets enthalten lassen.
Pakete können voneinander abhängen. Um das Paket foo
zu installieren, kann es erforderlich sein, eine bestimmte Version des Pakets bar
zu installieren. Das bar
-Paket kann selbst Abhängigkeiten haben, und in einigen Fällen können diese sogar kollidieren oder zyklische Abhängigkeiten bilden.
Da Node.js den realpath
aller Module, die es lädt, nachschlägt (d. h. es löst Symlinks auf) und dann nach ihren Abhängigkeiten in node_modules
-Ordnern sucht, kann diese Situation mit der folgenden Architektur gelöst werden:
/usr/lib/node/foo/1.2.3/
: Inhalt des Paketsfoo
, Version 1.2.3./usr/lib/node/bar/4.3.2/
: Inhalt des Paketsbar
, von demfoo
abhängt./usr/lib/node/foo/1.2.3/node_modules/bar
: Symbolischer Link zu/usr/lib/node/bar/4.3.2/
./usr/lib/node/bar/4.3.2/node_modules/*
: Symbolische Links zu den Paketen, von denenbar
abhängt.
Selbst wenn also ein Zyklus auftritt oder es zu Abhängigkeitskonflikten kommt, kann jedes Modul eine Version seiner Abhängigkeit erhalten, die es verwenden kann.
Wenn der Code im Paket foo
require('bar')
ausführt, erhält er die Version, die in /usr/lib/node/foo/1.2.3/node_modules/bar
verlinkt ist. Wenn dann der Code im Paket bar
require('quux')
aufruft, erhält er die Version, die in /usr/lib/node/bar/4.3.2/node_modules/quux
verlinkt ist.
Um den Modulsuchprozess noch optimaler zu gestalten, könnten wir die Pakete nicht direkt in /usr/lib/node
ablegen, sondern in /usr/lib/node_modules/\<name\>/\<version\>
. Dann wird Node.js nicht versuchen, fehlende Abhängigkeiten in /usr/node_modules
oder /node_modules
zu finden.
Um Module für die Node.js REPL verfügbar zu machen, könnte es nützlich sein, den Ordner /usr/lib/node_modules
auch der Umgebungsvariable $NODE_PATH
hinzuzufügen. Da die Modulsuchen mit node_modules
-Ordnern alle relativ sind und auf dem realen Pfad der Dateien basieren, die die Aufrufe von require()
tätigen, können sich die Pakete selbst überall befinden.
ECMAScript-Module mit require()
laden
[Historie]
Version | Änderungen |
---|---|
v23.5.0 | Diese Funktion gibt standardmäßig keine experimentelle Warnung mehr aus, obwohl die Warnung weiterhin durch --trace-require-module ausgegeben werden kann. |
v23.0.0 | Diese Funktion befindet sich nicht mehr hinter dem CLI-Flag --experimental-require-module . |
v23.0.0 | Unterstützung für den Interop-Export 'module.exports' in require(esm) . |
v22.0.0, v20.17.0 | Hinzugefügt in: v22.0.0, v20.17.0 |
[Stabil: 1 - Experimentell]
Stabil: 1 Stabilität: 1.2 - Release Candidate
Die .mjs
-Erweiterung ist für ECMAScript-Module reserviert. Weitere Informationen darüber, welche Dateien als ECMAScript-Module geparst werden, finden Sie im Abschnitt Bestimmen des Modulsystems.
require()
unterstützt nur das Laden von ECMAScript-Modulen, die die folgenden Anforderungen erfüllen:
- Das Modul ist vollständig synchron (enthält kein
await
auf oberster Ebene); und - Eine dieser Bedingungen ist erfüllt:
Wenn das geladene ES-Modul die Anforderungen erfüllt, kann require()
es laden und das Modul-Namespace-Objekt zurückgeben. In diesem Fall ähnelt es dem dynamischen import()
, wird aber synchron ausgeführt und gibt das Namespace-Objekt direkt zurück.
Mit den folgenden ES-Modulen:
// distance.mjs
export function distance(a, b) { return (b.x - a.x) ** 2 + (b.y - a.y) ** 2; }
// point.mjs
export default class Point {
constructor(x, y) { this.x = x; this.y = y; }
}
Ein CommonJS-Modul kann sie mit require()
laden:
const distance = require('./distance.mjs');
console.log(distance);
// [Module: null prototype] {
// distance: [Function: distance]
// }
const point = require('./point.mjs');
console.log(point);
// [Module: null prototype] {
// default: [class Point],
// __esModule: true,
// }
Zur Interoperabilität mit vorhandenen Tools, die ES-Module in CommonJS konvertieren, das dann echte ES-Module über require()
laden könnte, würde der zurückgegebene Namespace eine __esModule: true
-Eigenschaft enthalten, wenn er einen default
-Export hat, sodass verbrauchender Code, der von Tools generiert wurde, die Standardexporte in echten ES-Modulen erkennen kann. Wenn der Namespace bereits __esModule
definiert, wird dies nicht hinzugefügt. Diese Eigenschaft ist experimentell und kann sich in Zukunft ändern. Sie sollte nur von Tools verwendet werden, die ES-Module in CommonJS-Module konvertieren und dabei die bestehenden Ökosystemkonventionen befolgen. Code, der direkt in CommonJS geschrieben wurde, sollte es vermeiden, davon abhängig zu sein.
Wenn ein ES-Modul sowohl benannte Exporte als auch einen Standardexport enthält, ist das von require()
zurückgegebene Ergebnis das Modul-Namespace-Objekt, das den Standardexport in der Eigenschaft .default
platziert, ähnlich den von import()
zurückgegebenen Ergebnissen. Um anzupassen, was direkt von require(esm)
zurückgegeben werden soll, kann das ES-Modul den gewünschten Wert mit dem String-Namen "module.exports"
exportieren.
// point.mjs
export default class Point {
constructor(x, y) { this.x = x; this.y = y; }
}
// `distance` geht für CommonJS-Konsumenten dieses Moduls verloren, es sei denn, es wird
// als statische Eigenschaft zu `Point` hinzugefügt.
export function distance(a, b) { return (b.x - a.x) ** 2 + (b.y - a.y) ** 2; }
export { Point as 'module.exports' }
const Point = require('./point.mjs');
console.log(Point); // [class Point]
// Benannte Exporte gehen verloren, wenn 'module.exports' verwendet wird
const { distance } = require('./point.mjs');
console.log(distance); // undefined
Beachten Sie im obigen Beispiel, dass bei Verwendung des Exportnamens module.exports
benannte Exporte für CommonJS-Konsumenten verloren gehen. Damit CommonJS-Konsumenten weiterhin auf benannte Exporte zugreifen können, kann das Modul sicherstellen, dass der Standardexport ein Objekt ist, an das die benannten Exporte als Eigenschaften angehängt sind. Im obigen Beispiel kann beispielsweise distance
an den Standardexport, die Point
-Klasse, als statische Methode angehängt werden.
export function distance(a, b) { return (b.x - a.x) ** 2 + (b.y - a.y) ** 2; }
export default class Point {
constructor(x, y) { this.x = x; this.y = y; }
static distance = distance;
}
export { Point as 'module.exports' }
const Point = require('./point.mjs');
console.log(Point); // [class Point]
const { distance } = require('./point.mjs');
console.log(distance); // [Function: distance]
Wenn das Modul, das mit require()
geladen wird, await
auf oberster Ebene enthält oder der Modulgraf, den es import
iert, await
auf oberster Ebene enthält, wird ERR_REQUIRE_ASYNC_MODULE
ausgelöst. In diesem Fall sollten Benutzer das asynchrone Modul mit import()
laden.
Wenn --experimental-print-required-tla
aktiviert ist, wertet Node.js das Modul aus, anstatt vor der Auswertung ERR_REQUIRE_ASYNC_MODULE
auszulösen, versucht, die Awaits der obersten Ebene zu finden, und gibt deren Speicherort aus, um Benutzern bei der Behebung zu helfen.
Die Unterstützung für das Laden von ES-Modulen mit require()
ist derzeit experimentell und kann mit --no-experimental-require-module
deaktiviert werden. Um auszugeben, wo diese Funktion verwendet wird, verwenden Sie --trace-require-module
.
Diese Funktion kann erkannt werden, indem geprüft wird, ob process.features.require_module
true
ist.
Alles zusammen
Um den exakten Dateinamen zu erhalten, der geladen wird, wenn require()
aufgerufen wird, verwende die Funktion require.resolve()
.
Fasst man alles oben Genannte zusammen, so ergibt sich der folgende High-Level-Algorithmus in Pseudocode, was require()
tut:
require(X) von Modul am Pfad Y
1. Wenn X ein Kernmodul ist,
a. gib das Kernmodul zurück
b. STOP
2. Wenn X mit '/' beginnt
a. setze Y auf das Dateisystem-Root
3. Wenn X mit './' oder '/' oder '../' beginnt
a. LADE_ALS_DATEI(Y + X)
b. LADE_ALS_VERZEICHNIS(Y + X)
c. WERFE "nicht gefunden"
4. Wenn X mit '#' beginnt
a. LADE_PACKAGE_IMPORTS(X, dirname(Y))
5. LADE_PACKAGE_SELF(X, dirname(Y))
6. LADE_NODE_MODULES(X, dirname(Y))
7. WERFE "nicht gefunden"
VIELLEICHT_ERKENNEN_UND_LADEN(X)
1. Wenn X als CommonJS-Modul interpretiert werden kann, lade X als CommonJS-Modul. STOP.
2. Andernfalls, wenn der Quellcode von X als ECMAScript-Modul mit
<a href="esm.md#resolver-algorithm-specification">DETECT_MODULE_SYNTAX wie in der
ESM-Resolver-Spezifikation definiert</a> interpretiert werden kann,
a. Lade X als ECMAScript-Modul. STOP.
3. WERFE den SyntaxError vom Versuch, X als CommonJS in 1 zu interpretieren. STOP.
LADE_ALS_DATEI(X)
1. Wenn X eine Datei ist, lade X als sein Dateierweiterungsformat. STOP
2. Wenn X.js eine Datei ist,
a. Finde den nächstgelegenen Package-Scope SCOPE zu X.
b. Wenn kein Scope gefunden wurde
1. VIELLEICHT_ERKENNEN_UND_LADEN(X.js)
c. Wenn der SCOPE/package.json das Feld "type" enthält,
1. Wenn das Feld "type" "module" ist, lade X.js als ein ECMAScript-Modul. STOP.
2. Wenn das Feld "type" "commonjs" ist, lade X.js als ein CommonJS-Modul. STOP.
d. VIELLEICHT_ERKENNEN_UND_LADEN(X.js)
3. Wenn X.json eine Datei ist, lade X.json in ein JavaScript-Objekt. STOP
4. Wenn X.node eine Datei ist, lade X.node als binäres Addon. STOP
LADE_INDEX(X)
1. Wenn X/index.js eine Datei ist
a. Finde den nächstgelegenen Package-Scope SCOPE zu X.
b. Wenn kein Scope gefunden wurde, lade X/index.js als ein CommonJS-Modul. STOP.
c. Wenn der SCOPE/package.json das Feld "type" enthält,
1. Wenn das Feld "type" "module" ist, lade X/index.js als ein ECMAScript-Modul. STOP.
2. Andernfalls, lade X/index.js als ein CommonJS-Modul. STOP.
2. Wenn X/index.json eine Datei ist, parse X/index.json in ein JavaScript-Objekt. STOP
3. Wenn X/index.node eine Datei ist, lade X/index.node als binäres Addon. STOP
LADE_ALS_VERZEICHNIS(X)
1. Wenn X/package.json eine Datei ist,
a. Parse X/package.json, und suche nach dem Feld "main".
b. Wenn "main" ein falsy-Wert ist, gehe zu 2.
c. sei M = X + (json main Feld)
d. LADE_ALS_DATEI(M)
e. LADE_INDEX(M)
f. LADE_INDEX(X) VERALTET
g. WERFE "nicht gefunden"
2. LADE_INDEX(X)
LADE_NODE_MODULES(X, START)
1. sei DIRS = NODE_MODULES_PATHS(START)
2. für jedes DIR in DIRS:
a. LADE_PACKAGE_EXPORTS(X, DIR)
b. LADE_ALS_DATEI(DIR/X)
c. LADE_ALS_VERZEICHNIS(DIR/X)
NODE_MODULES_PATHS(START)
1. sei PARTS = path split(START)
2. sei I = Anzahl der PARTS - 1
3. sei DIRS = []
4. solange I >= 0,
a. wenn PARTS[I] = "node_modules", gehe zu d.
b. DIR = path join(PARTS[0 .. I] + "node_modules")
c. DIRS = DIR + DIRS
d. sei I = I - 1
5. gib DIRS + GLOBAL_FOLDERS zurück
LADE_PACKAGE_IMPORTS(X, DIR)
1. Finde den nächstgelegenen Package-Scope SCOPE zu DIR.
2. Wenn kein Scope gefunden wurde, gib zurück.
3. Wenn der SCOPE/package.json "imports" null oder undefiniert ist, gib zurück.
4. Wenn `--experimental-require-module` aktiviert ist
a. sei CONDITIONS = ["node", "require", "module-sync"]
b. Andernfalls, sei CONDITIONS = ["node", "require"]
5. sei MATCH = PACKAGE_IMPORTS_RESOLVE(X, pathToFileURL(SCOPE),
CONDITIONS) <a href="esm.md#resolver-algorithm-specification">wie in der ESM-Resolver-Spezifikation definiert</a>.
6. RESOLVE_ESM_MATCH(MATCH).
LADE_PACKAGE_EXPORTS(X, DIR)
1. Versuche, X als eine Kombination aus NAME und SUBPATH zu interpretieren, wobei der Name
ein @scope/-Präfix haben kann und der Subpfad mit einem Schrägstrich (`/`) beginnt.
2. Wenn X nicht mit diesem Muster übereinstimmt oder DIR/NAME/package.json keine Datei ist,
gib zurück.
3. Parse DIR/NAME/package.json, und suche nach dem Feld "exports".
4. Wenn "exports" null oder undefiniert ist, gib zurück.
5. Wenn `--experimental-require-module` aktiviert ist
a. sei CONDITIONS = ["node", "require", "module-sync"]
b. Andernfalls, sei CONDITIONS = ["node", "require"]
6. sei MATCH = PACKAGE_EXPORTS_RESOLVE(pathToFileURL(DIR/NAME), "." + SUBPATH,
`package.json` "exports", CONDITIONS) <a href="esm.md#resolver-algorithm-specification">wie in der ESM-Resolver-Spezifikation definiert</a>.
7. RESOLVE_ESM_MATCH(MATCH)
LADE_PACKAGE_SELF(X, DIR)
1. Finde den nächstgelegenen Package-Scope SCOPE zu DIR.
2. Wenn kein Scope gefunden wurde, gib zurück.
3. Wenn der SCOPE/package.json "exports" null oder undefiniert ist, gib zurück.
4. Wenn der SCOPE/package.json "name" nicht das erste Segment von X ist, gib zurück.
5. sei MATCH = PACKAGE_EXPORTS_RESOLVE(pathToFileURL(SCOPE),
"." + X.slice("name".length), `package.json` "exports", ["node", "require"])
<a href="esm.md#resolver-algorithm-specification">wie in der ESM-Resolver-Spezifikation definiert</a>.
6. RESOLVE_ESM_MATCH(MATCH)
RESOLVE_ESM_MATCH(MATCH)
1. sei RESOLVED_PATH = fileURLToPath(MATCH)
2. Wenn die Datei unter RESOLVED_PATH existiert, lade RESOLVED_PATH als sein Erweiterungsformat. STOP
3. WERFE "nicht gefunden"
Caching
Module werden nach dem ersten Laden zwischengespeichert. Dies bedeutet (unter anderem), dass jeder Aufruf von require('foo')
genau dasselbe Objekt zurückgibt, wenn er in dieselbe Datei aufgelöst würde.
Sofern require.cache
nicht geändert wird, führen mehrere Aufrufe von require('foo')
nicht dazu, dass der Modulcode mehrmals ausgeführt wird. Dies ist eine wichtige Funktion. Damit können "teilweise fertige" Objekte zurückgegeben werden, wodurch transitive Abhängigkeiten geladen werden können, selbst wenn sie Zyklen verursachen würden.
Um ein Modul mehrmals Code ausführen zu lassen, exportieren Sie eine Funktion und rufen Sie diese Funktion auf.
Einschränkungen beim Zwischenspeichern von Modulen
Module werden basierend auf ihrem aufgelösten Dateinamen zwischengespeichert. Da Module je nach Speicherort des aufrufenden Moduls (Laden aus node_modules
-Ordnern) in einen anderen Dateinamen aufgelöst werden können, ist es keine Garantie, dass require('foo')
immer genau dasselbe Objekt zurückgibt, wenn es in verschiedene Dateien aufgelöst würde.
Darüber hinaus können auf Dateisystemen oder Betriebssystemen, die keine Groß-/Kleinschreibung unterscheiden, verschiedene aufgelöste Dateinamen auf dieselbe Datei verweisen, aber der Cache behandelt sie dennoch als unterschiedliche Module und lädt die Datei mehrmals neu. Beispielsweise geben require('./foo')
und require('./FOO')
zwei verschiedene Objekte zurück, unabhängig davon, ob ./foo
und ./FOO
dieselbe Datei sind oder nicht.
Integrierte Module
[Historie]
Version | Änderungen |
---|---|
v16.0.0, v14.18.0 | node: -Importunterstützung zu require(...) hinzugefügt. |
Node.js verfügt über mehrere Module, die in die Binärdatei kompiliert sind. Diese Module werden an anderer Stelle in dieser Dokumentation ausführlicher beschrieben.
Die integrierten Module sind innerhalb des Node.js-Quellcodes definiert und befinden sich im Ordner lib/
.
Integrierte Module können anhand des Präfixes node:
identifiziert werden, wodurch der require
-Cache umgangen wird. Beispielsweise gibt require('node:http')
immer das integrierte HTTP-Modul zurück, selbst wenn ein Eintrag require.cache
mit diesem Namen vorhanden ist.
Einige integrierte Module werden immer bevorzugt geladen, wenn ihre Kennung an require()
übergeben wird. Beispielsweise gibt require('http')
immer das integrierte HTTP-Modul zurück, selbst wenn eine Datei mit diesem Namen vorhanden ist. Die Liste der integrierten Module, die ohne das Präfix node:
geladen werden können, wird in module.builtinModules
ohne das Präfix aufgeführt.
Integrierte Module mit obligatorischem Präfix node:
Beim Laden durch require()
müssen einige integrierte Module mit dem Präfix node:
angefordert werden. Diese Anforderung besteht, um zu verhindern, dass neu eingeführte integrierte Module in Konflikt mit User-Land-Paketen geraten, die den Namen bereits übernommen haben. Derzeit benötigen die folgenden integrierten Module das Präfix node:
:
Die Liste dieser Module wird in module.builtinModules
offengelegt, einschließlich des Präfixes.
Zyklen
Wenn zirkuläre require()
-Aufrufe vorhanden sind, ist ein Modul möglicherweise noch nicht vollständig ausgeführt, wenn es zurückgegeben wird.
Betrachten Sie diese Situation:
a.js
:
console.log('a starting');
exports.done = false;
const b = require('./b.js');
console.log('in a, b.done = %j', b.done);
exports.done = true;
console.log('a done');
b.js
:
console.log('b starting');
exports.done = false;
const a = require('./a.js');
console.log('in b, a.done = %j', a.done);
exports.done = true;
console.log('b done');
main.js
:
console.log('main starting');
const a = require('./a.js');
const b = require('./b.js');
console.log('in main, a.done = %j, b.done = %j', a.done, b.done);
Wenn main.js
a.js
lädt, lädt a.js
wiederum b.js
. An diesem Punkt versucht b.js
, a.js
zu laden. Um eine Endlosschleife zu verhindern, wird eine unvollendete Kopie des a.js
-Exportobjekts an das b.js
-Modul zurückgegeben. b.js
lädt dann vollständig, und sein exports
-Objekt wird dem a.js
-Modul bereitgestellt.
Wenn main.js
beide Module geladen hat, sind beide fertig. Die Ausgabe dieses Programms wäre also:
$ node main.js
main starting
a starting
b starting
in b, a.done = false
b done
in a, b.done = true
a done
in main, a.done = true, b.done = true
Sorgfältige Planung ist erforderlich, um sicherzustellen, dass zyklische Modulabhängigkeiten innerhalb einer Anwendung korrekt funktionieren.
Dateimodule
Wenn der exakte Dateiname nicht gefunden wird, versucht Node.js, den angeforderten Dateinamen mit den hinzugefügten Erweiterungen .js
, .json
und schließlich .node
zu laden. Wenn eine Datei mit einer anderen Erweiterung (z. B. .cjs
) geladen wird, muss ihr vollständiger Name an require()
übergeben werden, einschließlich der Dateierweiterung (z. B. require('./file.cjs')
).
.json
-Dateien werden als JSON-Textdateien geparst, .node
-Dateien werden als kompilierte Addon-Module interpretiert, die mit process.dlopen()
geladen werden. Dateien, die eine andere Erweiterung verwenden (oder gar keine Erweiterung), werden als JavaScript-Textdateien geparst. Im Abschnitt Modulsystem bestimmen wird erläutert, welches Parse-Ziel verwendet wird.
Ein angefordertes Modul mit dem Präfix '/'
ist ein absoluter Pfad zur Datei. Beispielsweise lädt require('/home/marco/foo.js')
die Datei unter /home/marco/foo.js
.
Ein angefordertes Modul mit dem Präfix './'
ist relativ zu der Datei, die require()
aufruft. Das heißt, circle.js
muss sich im selben Verzeichnis wie foo.js
befinden, damit require('./circle')
es finden kann.
Ohne ein führendes '/'
, './'
oder '../'
zur Angabe einer Datei muss das Modul entweder ein Kernmodul sein oder aus einem node_modules
-Ordner geladen werden.
Wenn der angegebene Pfad nicht existiert, wirft require()
einen MODULE_NOT_FOUND
-Fehler.
Ordner als Module
[Stabil: 3 - Legacy]
Stabil: 3 Stabilität: 3 - Legacy: Verwenden Sie stattdessen Subpath-Exporte oder Subpath-Importe.
Es gibt drei Möglichkeiten, wie ein Ordner als Argument an require()
übergeben werden kann.
Die erste besteht darin, eine package.json
-Datei im Stammverzeichnis des Ordners zu erstellen, die ein main
-Modul angibt. Eine Beispiel-package.json
-Datei könnte wie folgt aussehen:
{ "name" : "some-library",
"main" : "./lib/some-library.js" }
Wenn sich diese Datei in einem Ordner unter ./some-library
befände, würde require('./some-library')
versuchen, ./some-library/lib/some-library.js
zu laden.
Wenn keine package.json
-Datei im Verzeichnis vorhanden ist oder wenn der "main"
-Eintrag fehlt oder nicht aufgelöst werden kann, versucht Node.js, eine index.js
- oder index.node
-Datei aus diesem Verzeichnis zu laden. Wenn es im vorherigen Beispiel keine package.json
-Datei gäbe, würde require('./some-library')
beispielsweise versuchen, Folgendes zu laden:
./some-library/index.js
./some-library/index.node
Wenn diese Versuche fehlschlagen, meldet Node.js das gesamte Modul als fehlend mit der Standardfehlermeldung:
Error: Cannot find module 'some-library'
In allen drei oben genannten Fällen würde ein import('./some-library')
-Aufruf zu einem ERR_UNSUPPORTED_DIR_IMPORT
-Fehler führen. Die Verwendung von Package-Subpath-Exporten oder Subpath-Importen kann die gleichen Vorteile der Containment-Organisation wie Ordner als Module bieten und sowohl für require
als auch für import
funktionieren.
Laden aus node_modules
-Ordnern
Wenn der an require()
übergebene Modulbezeichner kein eingebautes Modul ist und nicht mit '/'
, '../'
oder './'
beginnt, beginnt Node.js im Verzeichnis des aktuellen Moduls, fügt /node_modules
hinzu und versucht, das Modul von diesem Speicherort zu laden. Node.js hängt node_modules
nicht an einen Pfad an, der bereits mit node_modules
endet.
Wenn es dort nicht gefunden wird, wechselt es zum übergeordneten Verzeichnis usw., bis die Wurzel des Dateisystems erreicht ist.
Wenn beispielsweise die Datei unter '/home/ry/projects/foo.js'
require('bar.js')
aufruft, würde Node.js an den folgenden Orten in dieser Reihenfolge suchen:
/home/ry/projects/node_modules/bar.js
/home/ry/node_modules/bar.js
/home/node_modules/bar.js
/node_modules/bar.js
Dies ermöglicht es Programmen, ihre Abhängigkeiten zu lokalisieren, so dass sie sich nicht überschneiden.
Es ist möglich, bestimmte Dateien oder Untermodule, die mit einem Modul verteilt werden, anzufordern, indem man nach dem Modulnamen ein Pfadsuffix angibt. Beispielsweise würde require('example-module/path/to/file')
path/to/file
relativ zu dem Ort auflösen, an dem sich example-module
befindet. Der angehängte Pfad folgt der gleichen Modulauflösungssemantik.
Laden aus den globalen Ordnern
Wenn die Umgebungsvariable NODE_PATH
auf eine durch Doppelpunkte getrennte Liste absoluter Pfade gesetzt ist, sucht Node.js in diesen Pfaden nach Modulen, wenn sie nicht anderswo gefunden werden.
Unter Windows wird NODE_PATH
durch Semikolons (;
) anstelle von Doppelpunkten getrennt.
NODE_PATH
wurde ursprünglich erstellt, um das Laden von Modulen aus verschiedenen Pfaden zu unterstützen, bevor der aktuelle Modulauflösungs-algorithmus definiert wurde.
NODE_PATH
wird immer noch unterstützt, ist aber weniger notwendig, seitdem sich das Node.js-Ökosystem auf eine Konvention für die Suche nach abhängigen Modulen geeinigt hat. Manchmal zeigen Bereitstellungen, die sich auf NODE_PATH
verlassen, überraschendes Verhalten, wenn die Leute sich nicht bewusst sind, dass NODE_PATH
gesetzt werden muss. Manchmal ändern sich die Abhängigkeiten eines Moduls, was dazu führt, dass eine andere Version (oder sogar ein anderes Modul) geladen wird, wenn die NODE_PATH
durchsucht wird.
Zusätzlich sucht Node.js in der folgenden Liste von GLOBAL_FOLDERS:
- 1:
$HOME/.node_modules
- 2:
$HOME/.node_libraries
- 3:
$PREFIX/lib/node
Dabei ist $HOME
das Home-Verzeichnis des Benutzers und $PREFIX
das für Node.js konfigurierte node_prefix
.
Diese sind hauptsächlich aus historischen Gründen vorhanden.
Es wird dringend empfohlen, Abhängigkeiten im lokalen node_modules
-Ordner zu platzieren. Diese werden schneller und zuverlässiger geladen.
Der Modul-Wrapper
Bevor der Code eines Moduls ausgeführt wird, umschließt Node.js ihn mit einem Funktions-Wrapper, der wie folgt aussieht:
(function(exports, require, module, __filename, __dirname) {
// Modulcode befindet sich tatsächlich hier
});
Dadurch erreicht Node.js ein paar Dinge:
- Es sorgt dafür, dass Variablen auf oberster Ebene (definiert mit
var
,const
oderlet
) auf das Modul und nicht auf das globale Objekt beschränkt sind. - Es hilft, einige global aussehende Variablen bereitzustellen, die tatsächlich spezifisch für das Modul sind, wie zum Beispiel:
- Die Objekte
module
undexports
, mit denen der Implementierer Werte aus dem Modul exportieren kann. - Die Komfortvariablen
__filename
und__dirname
, die den absoluten Dateinamen und den Verzeichnispfad des Moduls enthalten.
- Die Objekte
Der Modulbereich
__dirname
Hinzugefügt in: v0.1.27
Der Verzeichnisname des aktuellen Moduls. Dies ist dasselbe wie path.dirname()
von __filename
.
Beispiel: Ausführen von node example.js
von /Users/mjr
console.log(__dirname);
// Gibt aus: /Users/mjr
console.log(path.dirname(__filename));
// Gibt aus: /Users/mjr
__filename
Hinzugefügt in: v0.0.1
Der Dateiname des aktuellen Moduls. Dies ist der absolute Pfad der aktuellen Moduldatei mit aufgelösten Symlinks.
Für ein Hauptprogramm ist dies nicht unbedingt dasselbe wie der in der Befehlszeile verwendete Dateiname.
Siehe __dirname
für den Verzeichnisnamen des aktuellen Moduls.
Beispiele:
Ausführen von node example.js
von /Users/mjr
console.log(__filename);
// Gibt aus: /Users/mjr/example.js
console.log(__dirname);
// Gibt aus: /Users/mjr
Angenommen, es gibt zwei Module: a
und b
, wobei b
eine Abhängigkeit von a
ist und es eine Verzeichnisstruktur gibt von:
/Users/mjr/app/a.js
/Users/mjr/app/node_modules/b/b.js
Verweise auf __filename
innerhalb von b.js
geben /Users/mjr/app/node_modules/b/b.js
zurück, während Verweise auf __filename
innerhalb von a.js
/Users/mjr/app/a.js
zurückgeben.
exports
Hinzugefügt in: v0.1.12
Eine Referenz zu module.exports
, die kürzer zu tippen ist. Weitere Informationen zur Verwendung von exports
und module.exports
finden Sie im Abschnitt über die exports-Abkürzung.
module
Hinzugefügt in: v0.1.16
Eine Referenz zum aktuellen Modul, siehe den Abschnitt über das module
-Objekt. Insbesondere wird module.exports
verwendet, um zu definieren, was ein Modul exportiert und über require()
verfügbar macht.
require(id)
Hinzugefügt in: v0.1.13
Wird verwendet, um Module, JSON
und lokale Dateien zu importieren. Module können aus node_modules
importiert werden. Lokale Module und JSON-Dateien können über einen relativen Pfad importiert werden (z. B. ./
, ./foo
, ./bar/baz
, ../foo
), der relativ zu dem durch __dirname
(falls definiert) oder dem aktuellen Arbeitsverzeichnis angegebenen Verzeichnis aufgelöst wird. Die relativen Pfade im POSIX-Stil werden betriebssystemunabhängig aufgelöst, d. h. die obigen Beispiele funktionieren unter Windows genauso wie unter Unix-Systemen.
// Importieren eines lokalen Moduls mit einem Pfad relativ zu `__dirname` oder dem aktuellen
// Arbeitsverzeichnis. (Unter Windows würde dies zu .\path\myLocalModule aufgelöst.)
const myLocalModule = require('./path/myLocalModule');
// Importieren einer JSON-Datei:
const jsonData = require('./path/filename.json');
// Importieren eines Moduls aus node_modules oder einem integrierten Node.js-Modul:
const crypto = require('node:crypto');
require.cache
Hinzugefügt in: v0.3.0
Module werden in diesem Objekt zwischengespeichert, wenn sie benötigt werden. Durch Löschen eines Schlüsselwerts aus diesem Objekt wird das Modul beim nächsten require
neu geladen. Dies gilt nicht für native Addons, bei denen das Neuladen zu einem Fehler führt.
Das Hinzufügen oder Ersetzen von Einträgen ist ebenfalls möglich. Dieser Cache wird vor integrierten Modulen geprüft, und wenn ein Name, der mit einem integrierten Modul übereinstimmt, dem Cache hinzugefügt wird, erhalten nur node:
-präfixierte Require-Aufrufe das integrierte Modul. Mit Vorsicht verwenden!
const assert = require('node:assert');
const realFs = require('node:fs');
const fakeFs = {};
require.cache.fs = { exports: fakeFs };
assert.strictEqual(require('fs'), fakeFs);
assert.strictEqual(require('node:fs'), realFs);
require.extensions
Hinzugefügt in: v0.3.0
Veraltet seit: v0.10.6
[Stabil: 0 - Veraltet]
Stabil: 0 Stabilität: 0 - Veraltet
Weisen Sie require
an, wie bestimmte Dateiendungen behandelt werden sollen.
Verarbeiten Sie Dateien mit der Erweiterung .sjs
als .js
:
require.extensions['.sjs'] = require.extensions['.js'];
Veraltet. In der Vergangenheit wurde diese Liste verwendet, um Nicht-JavaScript-Module in Node.js zu laden, indem sie bei Bedarf kompiliert wurden. In der Praxis gibt es jedoch viel bessere Möglichkeiten, dies zu tun, z. B. das Laden von Modulen über ein anderes Node.js-Programm oder das Kompilieren zu JavaScript im Voraus.
Vermeiden Sie die Verwendung von require.extensions
. Die Verwendung kann zu subtilen Fehlern führen und das Auflösen der Erweiterungen wird mit jeder registrierten Erweiterung langsamer.
require.main
Hinzugefügt in: v0.1.17
Das Module
-Objekt, das das Einstiegsskript darstellt, das beim Starten des Node.js-Prozesses geladen wurde, oder undefined
, wenn der Einstiegspunkt des Programms kein CommonJS-Modul ist. Siehe "Zugriff auf das Hauptmodul".
Im entry.js
-Skript:
console.log(require.main);
node entry.js
Module {
id: '.',
path: '/absolute/path/to',
exports: {},
filename: '/absolute/path/to/entry.js',
loaded: false,
children: [],
paths:
[ '/absolute/path/to/node_modules',
'/absolute/path/node_modules',
'/absolute/node_modules',
'/node_modules' ] }
require.resolve(request[, options])
[Historie]
Version | Änderungen |
---|---|
v8.9.0 | Die Option paths wird jetzt unterstützt. |
v0.3.0 | Hinzugefügt in: v0.3.0 |
request
<string> Der aufzulösende Modulpfad.options
<Object>paths
<string[]> Pfade, von denen aus der Modulstandort aufgelöst werden soll. Falls vorhanden, werden diese Pfade anstelle der Standardauflösungspfade verwendet, mit Ausnahme von GLOBAL_FOLDERS wie$HOME/.node_modules
, die immer enthalten sind. Jeder dieser Pfade wird als Ausgangspunkt für den Modulauflösungsalgorithmus verwendet, was bedeutet, dass dienode_modules
-Hierarchie von diesem Speicherort aus überprüft wird.
Gibt zurück: <string>
Verwenden Sie die interne require()
-Maschinerie, um den Speicherort eines Moduls zu suchen, aber anstatt das Modul zu laden, geben Sie einfach den aufgelösten Dateinamen zurück.
Wenn das Modul nicht gefunden werden kann, wird ein MODULE_NOT_FOUND
-Fehler ausgelöst.
require.resolve.paths(request)
Hinzugefügt in: v8.9.0
request
<string> Der Modulpfad, dessen Suchpfade abgerufen werden.- Rückgabe: <string[]> | <null>
Gibt ein Array mit den Pfaden zurück, die während der Auflösung von request
durchsucht wurden, oder null
, wenn die Zeichenfolge request
auf ein Kernmodul verweist, z. B. http
oder fs
.
Das module
-Objekt
Hinzugefügt in: v0.1.16
In jedem Modul ist die freie Variable module
eine Referenz auf das Objekt, das das aktuelle Modul darstellt. Der Einfachheit halber ist module.exports
auch über die modulglobale Variable exports
zugänglich. module
ist eigentlich keine globale Variable, sondern lokal für jedes Modul.
module.children
Hinzugefügt in: v0.1.16
Die Modulobjekte, die von diesem Modul zum ersten Mal benötigt werden.
module.exports
Hinzugefügt in: v0.1.16
Das module.exports
-Objekt wird vom Module
-System erstellt. Manchmal ist dies nicht akzeptabel; viele möchten, dass ihr Modul eine Instanz einer bestimmten Klasse ist. Um dies zu tun, weisen Sie das gewünschte Exportobjekt module.exports
zu. Das Zuweisen des gewünschten Objekts zu exports
bindet lediglich die lokale exports
-Variable neu, was wahrscheinlich nicht erwünscht ist.
Nehmen wir zum Beispiel an, wir erstellen ein Modul namens a.js
:
const EventEmitter = require('node:events');
module.exports = new EventEmitter();
// Erledige etwas Arbeit und sende nach einiger Zeit
// das 'ready'-Ereignis vom Modul selbst.
setTimeout(() => {
module.exports.emit('ready');
}, 1000);
Dann könnten wir in einer anderen Datei Folgendes tun:
const a = require('./a');
a.on('ready', () => {
console.log('module "a" is ready');
});
Die Zuweisung zu module.exports
muss sofort erfolgen. Sie kann nicht in irgendwelchen Callbacks erfolgen. Das funktioniert nicht:
x.js
:
setTimeout(() => {
module.exports = { a: 'hello' };
}, 0);
y.js
:
const x = require('./x');
console.log(x.a);
exports
-Kurzbefehl
Hinzugefügt in: v0.1.16
Die Variable exports
ist innerhalb des Dateibereichs eines Moduls verfügbar und erhält vor der Auswertung des Moduls den Wert von module.exports
zugewiesen.
Sie ermöglicht eine Abkürzung, sodass module.exports.f = ...
prägnanter als exports.f = ...
geschrieben werden kann. Beachten Sie jedoch, dass wie bei jeder Variablen, wenn exports
ein neuer Wert zugewiesen wird, es nicht mehr an module.exports
gebunden ist:
module.exports.hello = true; // Exportiert von require des Moduls
exports = { hello: false }; // Nicht exportiert, nur im Modul verfügbar
Wenn die module.exports
-Eigenschaft vollständig durch ein neues Objekt ersetzt wird, ist es üblich, auch exports
neu zuzuweisen:
module.exports = exports = function Constructor() {
// ... usw.
};
Um das Verhalten zu veranschaulichen, stellen Sie sich diese hypothetische Implementierung von require()
vor, die der tatsächlichen Implementierung von require()
sehr ähnlich ist:
function require(/* ... */) {
const module = { exports: {} };
((module, exports) => {
// Modulcode hier. In diesem Beispiel wird eine Funktion definiert.
function someFunc() {}
exports = someFunc;
// An diesem Punkt ist exports keine Abkürzung mehr für module.exports, und
// dieses Modul exportiert weiterhin ein leeres Standardobjekt.
module.exports = someFunc;
// An diesem Punkt exportiert das Modul jetzt someFunc anstelle des
// Standardobjekts.
})(module, module.exports);
return module.exports;
}
module.filename
Hinzugefügt in: v0.1.16
Der vollständig aufgelöste Dateiname des Moduls.
module.id
Hinzugefügt in: v0.1.16
Die Kennung für das Modul. Typischerweise ist dies der vollständig aufgelöste Dateiname.
module.isPreloading
Hinzugefügt in: v15.4.0, v14.17.0
- Typ: <boolean>
true
, wenn das Modul während der Node.js-Vorladephase ausgeführt wird.
module.loaded
Hinzugefügt in: v0.1.16
Gibt an, ob das Modul bereits geladen wurde oder gerade geladen wird.
module.parent
Hinzugefügt in: v0.1.16
Veraltet seit: v14.6.0, v12.19.0
[Stabil: 0 - Veraltet]
Stabil: 0 Stabilität: 0 - Veraltet: Bitte verwenden Sie stattdessen require.main
und module.children
.
Das Modul, das dieses zuerst angefordert hat, oder null
, wenn das aktuelle Modul der Einstiegspunkt des aktuellen Prozesses ist, oder undefined
, wenn das Modul von etwas geladen wurde, das kein CommonJS-Modul ist (z. B.: REPL oder import
).
module.path
Hinzugefügt in: v11.14.0
Der Verzeichnisname des Moduls. Dies ist in der Regel das gleiche wie das path.dirname()
der module.id
.
module.paths
Hinzugefügt in: v0.4.0
Die Suchpfade für das Modul.
module.require(id)
Hinzugefügt in: v0.5.1
Die Methode module.require()
bietet eine Möglichkeit, ein Modul so zu laden, als ob require()
vom ursprünglichen Modul aufgerufen worden wäre.
Um dies zu tun, ist es notwendig, eine Referenz auf das module
-Objekt zu erhalten. Da require()
die module.exports
zurückgibt und das module
in der Regel nur innerhalb des Codes eines bestimmten Moduls verfügbar ist, muss es explizit exportiert werden, um verwendet werden zu können.
Das Module
-Objekt
Dieser Abschnitt wurde nach Module: module
Kernmodul verschoben.
Source Map v3 Unterstützung
Dieser Abschnitt wurde nach Module: module
Kernmodul verschoben.