Skip to content

Module: CommonJS-Module

[Stabil: 2 - Stabil]

Stabil: 2 Stabilität: 2 - Stabil

CommonJS-Module sind die ursprüngliche Methode, JavaScript-Code für Node.js zu verpacken. Node.js unterstützt auch den ECMAScript-Modul-Standard, der von Browsern und anderen JavaScript-Laufzeiten verwendet wird.

In Node.js wird jede Datei als separates Modul behandelt. Betrachten Sie beispielsweise eine Datei namens foo.js:

js
const circle = require('./circle.js')
console.log(`Die Fläche eines Kreises mit Radius 4 ist ${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:

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 Root eines Moduls hinzugefügt, indem zusätzliche Eigenschaften für das spezielle exports-Objekt angegeben werden.

Variablen, die lokal für das Modul sind, bleiben privat, da das Modul von Node.js in eine Funktion eingebunden wird (siehe Modulwrapper). In diesem Beispiel ist die Variable PI privat für circle.js.

Der Eigenschaft module.exports kann ein neuer Wert (wie eine Funktion oder ein Objekt) zugewiesen werden.

Im folgenden Code verwendet bar.js das square-Modul, das eine Square-Klasse exportiert:

js
const Square = require('./square.js')
const mySquare = new Square(2)
console.log(`Die Fläche von mySquare ist ${mySquare.area()}`)

Das square-Modul ist in square.js definiert:

js
// Die Zuweisung an exports verändert das Modul nicht, module.exports muss verwendet werden
module.exports = class Square {
  constructor(width) {
    this.width = width
  }

  area() {
    return this.width ** 2
  }
}

Das CommonJS-Modulsystem wird im module-Kernmodul implementiert.

Aktivieren

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ächste übergeordnete package.json-Datei ein Top-Level-Feld "type" mit dem Wert "commonjs" enthält.
  • Dateien mit der Erweiterung .js oder ohne Erweiterung, wenn die nächste übergeordnete package.json-Datei kein Top-Level-Feld "type" enthält oder sich keine package.json in einem übergeordneten Ordner befindet; es sei denn, die Datei enthält Syntax, die zu Fehlern führt, wenn sie nicht als ES-Modul ausgewertet wird. Paket-Autoren sollten das Feld "type" hinzufügen, auch in Paketen, in denen alle Quellen CommonJS sind. Die explizite Angabe des Typs des Pakets erleichtert Build-Tools und Loadern 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ächste übergeordnete package.json-Datei ein Top-Level-Feld "type" mit dem Wert "module" enthält, werden diese Dateien nur dann als CommonJS-Module erkannt, wenn sie über require() eingebunden werden, nicht wenn sie als Befehlszeilen-Einstiegspunkt 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 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 require.main === module getestet wird.

Für eine Datei foo.js ist dies true, wenn sie über node foo.js ausgeführt wird, aber false, wenn sie über require('./foo') ausgeführt wird.

Wenn der Einstiegspunkt kein CommonJS-Modul ist, ist require.main undefined, und das Hauptmodul ist unerreichbar.

Tipps für Paketmanager

Die Semantik der Node.js require()-Funktion wurde so gestaltet, dass sie allgemein genug ist, um vernünftige Verzeichnisstrukturen zu unterstützen. Paketmanager-Programme wie dpkg, rpm und npm sollten hoffentlich in der Lage sein, native Pakete aus Node.js-Modulen ohne Modifikation zu erstellen.

Im Folgenden geben wir eine vorgeschlagene Verzeichnisstruktur an, die funktionieren könnte:

Nehmen wir an, dass der Ordner /usr/lib/node/\<some-package\>/\<some-version\> den Inhalt einer bestimmten Version eines Pakets enthalten soll.

Pakete können voneinander abhängig sein. Um das Paket foo zu installieren, muss möglicherweise eine bestimmte Version des Pakets bar installiert werden. Das Paket bar 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 geladenen Module aufsucht (d. h. es löst Symlinks auf) und dann nach ihren Abhängigkeiten in node_modules-Ordnern sucht, kann diese Situation mit folgender Architektur gelöst werden:

  • /usr/lib/node/foo/1.2.3/: Inhalt des Pakets foo, Version 1.2.3.
  • /usr/lib/node/bar/4.3.2/: Inhalt des Pakets bar, von dem foo 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 denen bar abhängt.

So kann jedes Modul, selbst wenn ein Zyklus auftritt oder Abhängigkeitskonflikte bestehen, eine Version seiner Abhängigkeit erhalten, die es verwenden kann.

Wenn der Code im foo-Paket require('bar') ausführt, erhält es die Version, die in /usr/lib/node/foo/1.2.3/node_modules/bar verlinkt ist. Wenn dann der Code im bar-Paket require('quux') aufruft, erhält er die Version, die in /usr/lib/node/bar/4.3.2/node_modules/quux verlinkt ist.

Um den Modulsucheprozess noch optimaler zu gestalten, könnten wir die Pakete anstatt direkt in /usr/lib/node in /usr/lib/node_modules/\<name\>/\<version\> ablegen. Dann wird Node.js nicht versuchen, fehlende Abhängigkeiten in /usr/node_modules oder /node_modules zu suchen.

Um Module der Node.js REPL zur Verfügung zu stellen, könnte es sinnvoll sein, den Ordner /usr/lib/node_modules auch zur Umgebungsvariablen $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 an require() tätigen, können die Pakete selbst überall liegen.

Laden von ECMAScript-Modulen mit require()

[Verlauf]

VersionÄnderungen
v23.5.0Diese Funktion gibt standardmäßig keine experimentelle Warnung mehr aus, obwohl die Warnung weiterhin durch --trace-require-module ausgegeben werden kann.
v23.0.0Diese Funktion befindet sich nicht mehr hinter dem --experimental-require-module CLI-Flag.
v23.0.0Unterstützt 'module.exports' Interop-Export in require(esm).
v22.0.0, v20.17.0Hinzugefügt in: v22.0.0, v20.17.0

[Stabil: 1 - Experimentell]

Stabil: 1 Stabilität: 1.2 - Release Candidate

Die Erweiterung .mjs ist für ECMAScript-Module reserviert. Weitere Informationen dazu, welche Dateien als ECMAScript-Module analysiert werden, finden Sie im Abschnitt Bestimmung 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 Top-Level await); und
  • Eine dieser Bedingungen ist erfüllt:

Wenn das zu ladende 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:

js
// distance.mjs
export function distance(a, b) {
  return (b.x - a.x) ** 2 + (b.y - a.y) ** 2
}
js
// point.mjs
export default class Point {
  constructor(x, y) {
    this.x = x
    this.y = y
  }
}

Ein CommonJS-Modul kann diese mit require() laden:

js
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,
// }

Für die Interoperabilität mit bestehenden Tools, die ES-Module in CommonJS konvertieren, die dann echte ES-Module über require() laden könnten, würde der zurückgegebene Namespace eine Eigenschaft __esModule: true enthalten, wenn er einen default-Export hat, damit der von Tools generierte konsumierende Code die Standard-Exporte in echten ES-Modulen erkennen kann. Wenn der Namespace __esModule bereits definiert, wird dieser 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 bestehenden Ökosystemkonventionen folgen. Direkt in CommonJS verfasster Code sollte keine Abhängigkeit davon haben.

Wenn ein ES-Modul sowohl benannte Exporte als auch einen Standard-Export enthält, ist das von require() zurückgegebene Ergebnis das Modul-Namespace-Objekt, das den Standard-Export 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.

js
// 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' }
js
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 Standard-Export ein Objekt ist, an das die benannten Exporte als Eigenschaften angehängt sind. Zum Beispiel kann im obigen Beispiel distance an den Standard-Export, die Point-Klasse, als statische Methode angehängt werden.

js
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' }
js
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, Top-Level await enthält oder der Modulgraph, den es importiert, Top-Level await 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, versucht, die Top-Level-Awaits zu finden und gibt deren Position aus, um Benutzern bei der Behebung zu helfen, anstatt ERR_REQUIRE_ASYNC_MODULE vor der Auswertung auszulösen.

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.

Zusammenfassend

Um den genauen Dateinamen zu erhalten, der geladen wird, wenn require() aufgerufen wird, verwenden Sie die Funktion require.resolve().

Zusammenfassend hier der hochrangige Algorithmus in Pseudocode, was require() tut:

text
require(X) aus Modul im Pfad Y
1. Wenn X ein Core-Modul ist,
   a. gib das Core-Modul zurück
   b. STOP
2. Wenn X mit '/' beginnt
   a. setze Y auf das Dateisystem-Root
3. Wenn X mit './' oder '/' oder '../' beginnt
   a. LOAD_AS_FILE(Y + X)
   b. LOAD_AS_DIRECTORY(Y + X)
   c. WERFE "nicht gefunden"
4. Wenn X mit '#' beginnt
   a. LOAD_PACKAGE_IMPORTS(X, dirname(Y))
5. LOAD_PACKAGE_SELF(X, dirname(Y))
6. LOAD_NODE_MODULES(X, dirname(Y))
7. WERFE "nicht gefunden"

MAYBE_DETECT_AND_LOAD(X)
1. Wenn X als CommonJS-Modul geparst werden kann, lade X als CommonJS-Modul. STOP.
2. Andernfalls, wenn der Quellcode von X als ECMAScript-Modul geparst werden kann mit
  <a href="esm#resolver-algorithm-specification">DETECT_MODULE_SYNTAX definiert im
  ESM-Resolver</a>,
  a. Lade X als ECMAScript-Modul. STOP.
3. WERFE den SyntaxError aus dem Versuch, X als CommonJS in 1 zu parsen. STOP.

LOAD_AS_FILE(X)
1. Wenn X eine Datei ist, lade X im Format seiner Dateiendung. 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. MAYBE_DETECT_AND_LOAD(X.js)
    c. Wenn der SCOPE/package.json ein "type"-Feld enthält,
      1. Wenn das "type"-Feld "module" ist, lade X.js als ECMAScript-Modul. STOP.
      2. Wenn das "type"-Feld "commonjs" ist, lade X.js als CommonJS-Modul. STOP.
    d. MAYBE_DETECT_AND_LOAD(X.js)
3. Wenn X.json eine Datei ist, lade X.json als JavaScript-Objekt. STOP
4. Wenn X.node eine Datei ist, lade X.node als binäres Addon. STOP

LOAD_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 CommonJS-Modul. STOP.
    c. Wenn der SCOPE/package.json ein "type"-Feld enthält,
      1. Wenn das "type"-Feld "module" ist, lade X/index.js als ECMAScript-Modul. STOP.
      2. Andernfalls lade X/index.js als CommonJS-Modul. STOP.
2. Wenn X/index.json eine Datei ist, parse X/index.json zu einem JavaScript-Objekt. STOP
3. Wenn X/index.node eine Datei ist, lade X/index.node als binäres Addon. STOP

LOAD_AS_DIRECTORY(X)
1. Wenn X/package.json eine Datei ist,
   a. Parse X/package.json und suche nach dem "main"-Feld.
   b. Wenn "main" ein falsy Wert ist, GEHE ZU 2.
   c. sei M = X + (json main Feld)
   d. LOAD_AS_FILE(M)
   e. LOAD_INDEX(M)
   f. LOAD_INDEX(X) VERALTET
   g. WERFE "nicht gefunden"
2. LOAD_INDEX(X)

LOAD_NODE_MODULES(X, START)
1. sei DIRS = NODE_MODULES_PATHS(START)
2. für jedes DIR in DIRS:
   a. LOAD_PACKAGE_EXPORTS(X, DIR)
   b. LOAD_AS_FILE(DIR/X)
   c. LOAD_AS_DIRECTORY(DIR/X)

NODE_MODULES_PATHS(START)
1. sei PARTS = Pfad 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 = Pfad join(PARTS[0 .. I] + "node_modules")
   c. DIRS = DIR + DIRS
   d. sei I = I - 1
5. gib DIRS + GLOBAL_FOLDERS zurück

LOAD_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#resolver-algorithm-specification">definiert im ESM-Resolver</a>.
6. RESOLVE_ESM_MATCH(MATCH).

LOAD_PACKAGE_EXPORTS(X, DIR)
1. Versuche, X als Kombination aus NAME und SUBPATH zu interpretieren, wobei der Name
   ein @scope/-Präfix haben kann und der Subpath mit einem Schrägstrich (`/`) beginnt.
2. Wenn X diesem Muster nicht entspricht oder DIR/NAME/package.json keine Datei ist,
   gib zurück.
3. Parse DIR/NAME/package.json und suche nach dem "exports"-Feld.
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#resolver-algorithm-specification">definiert im ESM-Resolver</a>.
7. RESOLVE_ESM_MATCH(MATCH)

LOAD_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#resolver-algorithm-specification">definiert im ESM-Resolver</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 im Format seiner Erweiterung. STOP
3. WERFE "nicht gefunden"

Caching

Module werden nach dem ersten Laden zwischengespeichert. Das bedeutet (unter anderem), dass jeder Aufruf von require('foo') genau dasselbe Objekt zurückgibt, wenn er zur gleichen Datei führen würde.

Sofern require.cache nicht verändert wird, führen mehrere Aufrufe von require('foo') nicht dazu, dass der Modulcode mehrmals ausgeführt wird. Dies ist ein wichtiges Feature. Damit können "teilweise fertiggestellte" Objekte zurückgegeben werden, wodurch das Laden transitiver Abhängigkeiten auch dann ermöglicht wird, wenn sie Zyklen verursachen würden.

Um einen Modulcode mehrmals auszuführen, exportieren Sie eine Funktion und rufen Sie diese Funktion auf.

Einschränkungen beim Modul-Caching

Module werden basierend auf ihrem aufgelösten Dateinamen zwischengespeichert. Da Module je nach Position des aufrufenden Moduls (Laden aus node_modules-Ordnern) zu einem anderen Dateinamen aufgelöst werden können, ist es keine Garantie, dass require('foo') immer genau dasselbe Objekt zurückgibt, wenn es zu verschiedenen Dateien führen würde.

Zusätzlich können auf datei-unabhängigen Dateisystemen oder Betriebssystemen verschiedene aufgelöste Dateinamen auf dieselbe Datei verweisen, aber der Cache behandelt sie dennoch als verschiedene 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.

Eingebaute Module

[Historie]

VersionÄnderungen
v16.0.0, v14.18.0Unterstützung für node:-Importe 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 detaillierter beschrieben.

Die eingebauten Module sind im Node.js-Quellcode definiert und befinden sich im Ordner lib/.

Eingebaute Module können mit dem Präfix node: identifiziert werden. In diesem Fall wird der require-Cache umgangen. Beispielsweise gibt require('node:http') immer das eingebaute HTTP-Modul zurück, selbst wenn ein Eintrag mit diesem Namen in require.cache vorhanden ist.

Einige eingebaute Module werden immer bevorzugt geladen, wenn ihre Kennung an require() übergeben wird. Beispielsweise gibt require('http') immer das eingebaute HTTP-Modul zurück, selbst wenn eine Datei mit diesem Namen existiert. Die Liste der eingebauten Module, die ohne Verwendung des Präfixes node: geladen werden können, wird in module.builtinModules ohne das Präfix aufgeführt.

Integrierte Module mit obligatorischem node:-Präfix

Beim Laden über 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 mit Paketen im Benutzerbereich kollidieren, die den Namen bereits verwendet haben. Derzeit benötigen die folgenden integrierten Module das node:-Präfix:

Die Liste dieser Module wird in module.builtinModules inklusive des Präfixes angezeigt.

Zyklen

Bei zirkulären require()-Aufrufen ist ein Modul möglicherweise noch nicht vollständig ausgeführt, wenn es zurückgegeben wird.

Betrachten Sie diese Situation:

a.js:

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:

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:

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 unvollständige Kopie des a.js-Exportsobjekts an das b.js-Modul zurückgegeben. b.js wird dann fertig geladen, 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 daher:

bash
$ 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

Eine sorgfältige Planung ist erforderlich, damit zykliche Modulhängigkeiten innerhalb einer Anwendung korrekt funktionieren.

Dateimodul

Wird die genaue Dateinamen nicht gefunden, versucht Node.js, den benötigten Dateinamen mit den zusätzlichen Erweiterungen .js, .json und schließlich .node zu laden. Beim Laden einer Datei mit einer anderen Erweiterung (z. B. .cjs) muss ihr vollständiger Name einschließlich der Dateierweiterung an require() übergeben werden (z. B. require('./file.cjs')).

.json-Dateien werden als JSON-Textdateien analysiert, .node-Dateien werden als kompilierte Addon-Module interpretiert, die mit process.dlopen() geladen werden. Dateien mit einer anderen Erweiterung (oder ohne Erweiterung) werden als JavaScript-Textdateien analysiert. Informationen zum verwendeten Analyse-Ziel finden Sie im Abschnitt Bestimmung des Modulsystems.

Ein benötigtes Modul, dem ein / vorangestellt ist, ist ein absoluter Pfad zur Datei. Beispielsweise lädt require('/home/marco/foo.js') die Datei unter /home/marco/foo.js.

Ein benötigtes Modul, dem './' vorangestellt ist, 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 findet.

Ohne ein führendes /, './' oder '../', um eine Datei anzugeben, muss das Modul entweder ein Kernmodul sein oder aus einem node_modules-Ordner geladen werden.

Wenn der angegebene Pfad nicht existiert, löst require() einen MODULE_NOT_FOUND-Fehler aus.

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 so aussehen:

json
{ "name": "some-library", "main": "./lib/some-library.js" }

Befände sich dies in einem Ordner unter ./some-library, würde require('./some-library') versuchen, ./some-library/lib/some-library.js zu laden.

Wenn keine package.json-Datei im Verzeichnis vorhanden ist oder der Eintrag "main" fehlt oder nicht aufgelöst werden kann, versucht Node.js, eine index.js- oder index.node-Datei aus diesem Verzeichnis zu laden. Wenn beispielsweise im vorherigen Beispiel keine package.json-Datei vorhanden wäre, würde require('./some-library') versuchen 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:

bash
Error: Cannot find module 'some-library'

In allen drei oben genannten Fällen würde ein Aufruf von import('./some-library') zu einem ERR_UNSUPPORTED_DIR_IMPORT-Fehler führen. Die Verwendung von Package-Subpath-Exporten oder Subpath-Importen kann die gleichen Vorteile der Kapselung 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.

Wird es dort nicht gefunden, wechselt es zum übergeordneten Verzeichnis und so weiter, bis die Wurzel des Dateisystems erreicht ist.

Wenn beispielsweise die Datei unter '/home/ry/projects/foo.js' require('bar.js') aufgerufen hat, würde Node.js in der folgenden Reihenfolge nach den folgenden Speicherorten 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, sodass keine Konflikte auftreten.

Es ist möglich, spezifische Dateien oder Untermodule, die mit einem Modul verteilt werden, anzufordern, indem ein Pfadsuffix nach dem Modulnamen eingefügt wird. 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 Modul-Auflö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 an anderer Stelle nicht gefunden werden.

Unter Windows wird NODE_PATH durch Semikolons (;) anstelle von Doppelpunkten getrennt.

NODE_PATH wurde ursprünglich entwickelt, um das Laden von Modulen aus verschiedenen Pfaden zu unterstützen, bevor der aktuelle Algorithmus zur Modulauflösung definiert wurde.

NODE_PATH wird weiterhin unterstützt, ist aber jetzt weniger notwendig, da sich das Node.js-Ökosystem auf eine Konvention zum Auffinden abhängiger Module geeinigt hat. Manchmal zeigen Deployments, die auf NODE_PATH angewiesen sind, überraschendes Verhalten, wenn die Benutzer nicht wissen, dass NODE_PATH gesetzt werden muss. Manchmal ändern sich die Abhängigkeiten eines Moduls, wodurch eine andere Version (oder sogar ein anderes Modul) geladen wird, während 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

Wobei $HOME das Home-Verzeichnis des Benutzers und $PREFIX das von Node.js konfigurierte node_prefix ist.

Dies geschieht hauptsächlich aus historischen Gründen.

Es wird dringend empfohlen, Abhängigkeiten im lokalen Ordner node_modules 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 einer Funktionshülle, die wie folgt aussieht:

js
;(function (exports, require, module, __filename, __dirname) {
  // Der Modulcode befindet sich hier
})

Dadurch erreicht Node.js einige Dinge:

  • Es hält Variablen der obersten Ebene (definiert mit var, const oder let) im Bereich des Moduls und nicht des globalen Objekts.
  • Es hilft, einige global erscheinende Variablen bereitzustellen, die tatsächlich modul spezifisch sind, wie zum Beispiel:
    • Die Objekte module und exports, die der Implementierer verwenden kann, um Werte aus dem Modul zu exportieren.
    • Die Komfortvariablen __filename und __dirname, die den absoluten Dateinamen und den Verzeichnispfad des Moduls enthalten.

Der Modul-Scope

__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 aus /Users/mjr

js
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 symbolischen Links.

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 aus /Users/mjr

js
console.log(__filename)
// Gibt aus: /Users/mjr/example.js
console.log(__dirname)
// Gibt aus: /Users/mjr

Gegeben seien 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

Referenzen auf __filename innerhalb von b.js geben /Users/mjr/app/node_modules/b/b.js zurück, während Referenzen auf __filename innerhalb von a.js /Users/mjr/app/a.js zurückgeben.

exports

Hinzugefügt in: v0.1.12

Ein Verweis auf module.exports, der kürzer zu tippen ist. Siehe den Abschnitt über den exports-Shortcut für Details, wann exports und wann module.exports verwendet werden sollte.

module

Hinzugefügt in: v0.1.16

Ein Verweis auf das aktuelle Modul, siehe den Abschnitt über das module-Objekt. Insbesondere wird module.exports verwendet, um zu definieren, was ein Modul exportiert und über require() zur Verfügung stellt.

require(id)

Hinzugefügt in: v0.1.13

  • id <string> Modulname oder Pfad
  • Rückgabewert: <any> Exportierter Modul-Inhalt

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 mit einem relativen Pfad importiert werden (z. B. ./, ./foo, ./bar/baz, ../foo), der anhand des durch __dirname (falls definiert) oder das aktuelle Arbeitsverzeichnis angegebenen Verzeichnisses aufgelöst wird. Die relativen Pfade im POSIX-Stil werden plattformunabhängig aufgelöst, d. h. die obigen Beispiele funktionieren unter Windows genauso wie unter Unix-Systemen.

js
// Importiert ein lokales Modul mit einem Pfad relativ zu `__dirname` oder dem aktuellen
// Arbeitsverzeichnis. (Unter Windows würde dies zu .\path\myLocalModule aufgelöst werden.)
const myLocalModule = require('./path/myLocalModule')

// Importiert eine JSON-Datei:
const jsonData = require('./path/filename.json')

// Importiert ein Modul aus node_modules oder ein integriertes 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üssel-Wert-Paares aus diesem Objekt wird das Modul beim nächsten require neu geladen. Dies gilt nicht für native Addons, bei denen ein erneutes Laden 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!

js
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

Weist require an, wie bestimmte Dateiendungen behandelt werden sollen.

Verarbeitung von Dateien mit der Erweiterung .sjs als .js:

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 vorherige Kompilieren in JavaScript.

Vermeiden Sie die Verwendung von require.extensions. Die Verwendung kann zu subtilen Fehlern führen und die Auflösung der Erweiterungen wird mit jeder registrierten Erweiterung langsamer.

require.main

Hinzugefügt in: v0.1.17

Das Module-Objekt, das das beim Start des Node.js-Prozesses geladene Einstiegsskript darstellt, oder undefined, wenn der Einstiegspunkt des Programms kein CommonJS-Modul ist. Siehe "Zugriff auf das Hauptmodul".

Im entry.js-Skript:

js
console.log(require.main)
bash
node entry.js
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])

[Verlauf]

VersionÄnderungen
v8.9.0Die Option paths wird jetzt unterstützt.
v0.3.0Hinzugefügt in: v0.3.0
  • request <Zeichenkette> Der aufzulösende Modulpfad.

  • options <Objekt>

    • paths <Zeichenkette[]> Pfade zur Auflösung des Modulstandorts. Wenn 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, d. h. die node_modules-Hierarchie wird von diesem Speicherort aus überprüft.
  • Rückgabewert: <Zeichenkette>

Verwenden Sie den internen Mechanismus von require(), um den Speicherort eines Moduls zu suchen, aber geben Sie anstatt das Modul zu laden nur 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

Gibt ein Array zurück, das die während der Auflösung von request durchsuchten Pfade enthält, oder null, wenn die request-Zeichenfolge 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 ein Verweis auf das Objekt, das das aktuelle Modul darstellt. Der Einfachheit halber ist module.exports auch über das Modul-Globale exports zugänglich. module ist nicht wirklich global, sondern lokal für jedes Modul.

module.children

Hinzugefügt in: v0.1.16

Die Modul-Objekte, 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 Export-Objekt module.exports zu. Die Zuweisung des gewünschten Objekts an exports bindet einfach die lokale Variable exports neu, was wahrscheinlich nicht gewünscht ist.

Nehmen wir zum Beispiel an, wir erstellen ein Modul namens a.js:

js
const EventEmitter = require('node:events')

module.exports = new EventEmitter()

// Etwas Arbeit erledigen und nach einiger Zeit
// das 'ready'-Ereignis vom Modul selbst auslösen.
setTimeout(() => {
  module.exports.emit('ready')
}, 1000)

Dann könnten wir in einer anderen Datei Folgendes tun:

js
const a = require('./a')
a.on('ready', () => {
  console.log('Modul "a" ist bereit')
})

Die Zuweisung an module.exports muss sofort erfolgen. Sie kann nicht in irgendwelchen Callbacks erfolgen. Dies funktioniert nicht:

x.js:

js
setTimeout(() => {
  module.exports = { a: 'hello' }
}, 0)

y.js:

js
const x = require('./x')
console.log(x.a)

exports-Abkürzung

Hinzugefügt in: v0.1.16

Die Variable exports ist innerhalb des Datei-Level-Scopes eines Moduls verfügbar und erhält den Wert von module.exports, bevor das Modul ausgewertet wird.

Sie ermöglicht eine Abkürzung, so dass module.exports.f = ... prägnanter als exports.f = ... geschrieben werden kann. Beachten Sie jedoch, dass, wie bei jeder Variable, wenn ein neuer Wert exports zugewiesen wird, dieser nicht mehr an module.exports gebunden ist:

js
module.exports.hello = true // Exportiert aus require des Moduls
exports = { hello: false } // Nicht exportiert, nur im Modul verfügbar

Wenn die Eigenschaft module.exports vollständig durch ein neues Objekt ersetzt wird, wird üblicherweise auch exports neu zugewiesen:

js
module.exports = exports = function Constructor() {
  // ... etc.
}

Um das Verhalten zu veranschaulichen, stellen Sie sich diese hypothetische Implementierung von require() vor, die der tatsächlichen Implementierung von require() sehr ähnlich ist:

js
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 nun 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. Normalerweise 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-Preload-Phase läuft.

module.loaded

Hinzugefügt in: v0.1.16

Ob das Modul fertig geladen ist oder sich im Ladevorgang befindet.

module.parent

Hinzugefügt in: v0.1.16

Seit v14.6.0, v12.19.0 veraltet

[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 normalerweise dasselbe wie path.dirname() von 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 zu laden, als ob require() aus dem ursprünglichen Modul aufgerufen worden wäre.

Um dies zu tun, ist es notwendig, eine Referenz auf das module-Objekt zu erhalten. Da require() module.exports zurückgibt und das module typischerweise 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 verschoben nach Module: module-Kernmodul.

Unterstützung für Source Maps v3

Dieser Abschnitt wurde verschoben nach Module: module-Kernmodul.