Node.js-Modulsystem

Das Node.js-Modulsystem ist eine seiner Kernfunktionen und ermöglicht Entwicklern, Code in kleine, wiederverwendbare Einheiten namens Module zu organisieren.

Ein Modul ist eine unabhängige Datei, die bestimmte Funktionalität kapselt und in anderen Dateien importiert und verwendet werden kann.

Das Node.js-Modulsystem ist eine wichtige Grundlage für die Erstellung von Node.js-Anwendungen und ermöglicht Entwicklern, Code in wiederverwendbare, wartbare Teile aufzuteilen.

Jedes Modul ist eine separate JavaScript-Datei, die eine Reihe zusammengehöriger Funktionen, Objekte oder Variablen enthält. Auf diese Weise bietet Node.js einen strukturierten Ansatz zur Verwaltung großer Anwendungen, wodurch der Code klarer und einfacher zu warten ist.

Jede Datei in Node.js kann als Modul fungieren und befindet sich standardmäßig in einem unabhängigen Gültigkeitsbereich.

Das Modulsystem folgt der CommonJS-Spezifikation, unterstützt aber auch ES-Module.

Modultypen

Node.js unterstützt die folgenden Modultypen:

  • Integrierte Module: Module, die mit Node.js geliefert werden, wie z. B.fs、http、pathusw.
  • Benutzerdefinierte Module: Von Entwicklern erstellte Module.
  • Module von Drittanbietern: Über npm installierte Module, wie z. B.express、lodashusw.

Integrierte Module importieren

Integrierte Module sind Module, die mit Node.js geliefert werden und bereits in der Umgebung enthalten sind, wenn Node.js installiert wird, sodass keine zusätzliche Installation erforderlich ist. Häufige integrierte Module sind fs, http, path, os, crypto usw.

Um ein integriertes Modul zu importieren, müssen Sie lediglich dierequire()-Funktion verwenden und den Modulnamen übergeben. Um beispielsweise das Dateisystemmodulfs、path、http, können Sie Folgendes tun:

// 导入文件系统模块
const fs = require('fs');

// 导入路径模块
const path = require('path');

// 导入 HTTP 模块
const http = require('http');

Hier ist fs eine Zeichenkette, die den Namen des zu ladenden Moduls darstellt. Node.js erkennt es automatisch als integriertes Modul und lädt die entsprechende Funktionalität.

Beispiel

const os = require('os');
console.log('Operating System:', os.platform());
console.log('Total Memory:', os.totalmem());

Module von Drittanbietern importieren

Module von Drittanbietern sind Module, die von Entwicklern oder der Open-Source-Community veröffentlicht werden und über npm (Node Package Manager) in ein Projekt installiert werden können. Häufige Module von Drittanbietern sind express, lodash, axios usw.

Sobald Module von Drittanbietern installiert sind, werden sie im node_modules-Verzeichnis des Projekts abgelegt. Um ein Modul eines Drittanbieters zu importieren, verwenden Sie ebenfalls dierequire()-Funktion, übergeben Sie jedoch den Modulnamen. Um beispielsweise das express-Framework zu importieren, können Sie Folgendes tun:

// 导入第三方模块 express
const express = require('express');

Bevor Sie ein Modul eines Drittanbieters importieren, stellen Sie sicher, dass Sie es über npm installiert haben. Zum Beispiel kann die Installation von express mit dem folgenden Befehl erfolgen:

npm install express

Installieren Sie das Drittanbieter-Modul lodash und importieren Sie es zum Testen:

Beispiel

const lodash = require('lodash');
const array = [1, 2, 3, 4];
const reversedArray = lodash.reverse(array.slice()); // Verwende lodash, um das Array umzukehren
console.log('Reversed Array:', reversedArray);

Export und Import benutzerdefinierter Module (CommonJS-Module)

1. Module exportieren:Verwenden Siemodule.exportsoderexports, um Funktionen, Objekte oder Variablen zu exportieren.

2. Module importieren:Verwenden Sierequire(), um Module zu importieren.

Module importieren

In Node.js ist das Importieren eines Moduls sehr einfach. Unten erstellen wir einemain.js-Datei und importieren das hello-Modul. Der Code lautet wie folgt:

Code der Datei main.js:

var hello = require('./hello');
hello.world();

Im obigen Beispiel importiert der Coderequire('./hello')diehello.js-Datei im aktuellen Verzeichnis (./ist das aktuelle Verzeichnis, und die Standarderweiterung für Node.js-Dateien ist.js)。

Node.js stellt zwei Objekte bereit, exports und require. exports ist die öffentliche Schnittstelle eines Moduls, und require wird verwendet, um die Schnittstelle eines Moduls von außen zu erhalten, d. h. das exports-Objekt des Moduls, das angefordert wird.

Als Nächstes erstellen wir die Datei hello.js. Der Code lautet wie folgt:

Code der Datei hello.js:

exports.world = function() {
    console.log('Hello World');
}

Im obigen Beispiel verwendet hello.js das exports-Objekt, um world zur Zugriffsschnittstelle des Moduls zu machen. In main.js können wir durchrequire('./hello')dieses Modul laden und dann direkt auf die Memberfunktionen desexports-Objekts in hello.js zugreifen.

Manchmal möchten wir einfach ein Objekt in einem Modul kapseln. Das Format ist wie folgt:

module.exports = function() {
  // ...
}

Zum Beispiel:

Code der Datei hello.js:

// hello.js
function Hello() {
        var name;
        this.setName = function(thyName) {
                name = thyName;
        };
        this.sayHello = function() {
                console.log('Hello ' + name);
        };
};
module.exports = Hello;

Auf diese Weise können Sie dieses Objekt direkt erhalten:

Code der Datei main.js:

// main.js
var Hello = require('./hello');
hello = new Hello();
hello.setName('BYVoid');
hello.sayHello();

Die einzige Änderung an der Modulschnittstelle ist die Verwendung vonmodule.exports = Helloanstelle vonexports.world = function(){}. Wenn extern auf das Modul verwiesen wird, ist sein Schnittstellenobjekt das zu exportierende Hello-Objekt selbst und nicht das ursprüngliche exports.

Der Unterschied zwischen module.exports und exports:

  • module.exportsist die tatsächliche Referenz auf das exportierte Objekt.
  • exportsJamodule.exportsist eine Kurzform dafür. Man kann nicht direkt zuweisenexports = ..., andernfalls wird die Referenz zerstört.

Benutzerdefinierter Modulexport und -import (ES-Module)

ES-Module verwendenimportundexport, was die moderne JavaScript-Modulspezifikation ist.

  • ES-Module verwenden dasimportundexportSchlüsselwort, und die Dateiendung muss festgelegt werden auf.mjs, oder inpackage.jsondeclare"type": "module"。
  • ES-Module unterstützen statische Importe (import ... from ...) und dynamische Importe (import())。

Beispiel

// myModule.mjs
export function greet(name) {
  return `Hello, ${name}!`;
}

// main.mjs
import { greet } from './myModule.mjs';
console.log(greet('Bob')); // Ausgabe: Hallo, Bob!

Pfadauflösung zum Laden von Modulen

1. Kernmodule:Wie http und fs, die bei der Installation von Node.js enthalten sind und direkt geladen werden können.

const http = require('http');

2. Lokale Dateimodule:Verwenden Sie zum Laden relative oder absolute Pfade; Sie müssen./oder/。

const myModule = require('./myModule');

3. Drittanbieter-Module:Sie befinden sich imnode_modulesVerzeichnis; Sie müssen nur den Modulnamen eingeben, um sie zu laden.

const express = require('express');

Modul-Caching

Modul-Caching-Mechanismus:Node.js speichert geladene Module zwischen, um die Leistung zu verbessern. Wenn require() erneut für dasselbe Modul aufgerufen wird, wird das zwischengespeicherte Modul direkt zurückgegeben, anstatt es erneut zu laden.

Aktualisieren des Caches:Um ein Modul neu zu laden, können Sie den Cache löschen:

delete require.cache[require.resolve('./myModule')];

Zirkuläre Abhängigkeiten

Wenn zwei oder mehr Module sich gegenseitig importieren, spricht man von einer zirkulären Abhängigkeit. Node.js kann einfache zirkuläre Abhängigkeiten verarbeiten, aber es kann dazu führen, dass von einigen Modulen exportierte Objekte nicht vollständig initialisiert sind.

Beispiel

// a.js
const b = require('./b');
console.log('a.js:', b.message);
module.exports = { message: 'Hello from a' };

// b.js
const a = require('./a');
console.log('b.js:', a.message);
module.exports = { message: 'Hello from b' };

// main.js
require('./a');

Die Ausgabe zeigt, dass, wenn b.js a.js importiert, da a.js noch nicht vollständig ausgeführt wurde, a.message undefined ist.


Modul-Wrapping und Gültigkeitsbereich

Node.js schließt jedes Modul in eine Funktion ein und verleiht jedem Modul einen eigenständigen Gültigkeitsbereich. Beispiel:

// Node.js 内部将模块包装为类似这样的函数:
(function(exports, require, module, __filename, __dirname) {
  // 模块代码在这里
});

Das bedeutet, dass in einem Modul definierte Variablen nicht den globalen Gültigkeitsbereich verschmutzen.


Wo werden serverseitige Module platziert?

Vielleicht ist Ihnen aufgefallen, dass wir in unserem Code bereits Module verwendet haben. So wie hier:

var http = require("http");

...

http.createServer(...);

Node.js wird mit einem Modul namenshttpgeliefert. Wir binden es in unseren Code ein und weisen den Rückgabewert einer lokalen Variablen zu.

Dadurch wird unsere lokale Variable zu einem Objekt, das alle vom http-Modul bereitgestellten öffentlichen Methoden enthält.

Die Dateisuchestrategie der require-Methode von Node.js ist wie folgt:

Da es in Node.js 4 Arten von Modulen gibt (native Module und 3 Arten von Dateimodulen), ist das interne Laden, obwohl die require-Methode extrem einfach ist, recht komplex, und die Ladeprioritäten unterscheiden sich. Wie im folgenden Diagramm gezeigt:

Laden aus dem Dateimodul-Cache

Obwohl native Module und Dateimodule unterschiedliche Prioritäten haben, laden beide zuerst vorhandene Module aus dem Dateimodul-Cache.

Laden aus nativen Modulen

Native Module haben eine Priorität, die nur noch vom Dateimodul-Cache übertroffen wird. Nachdem die require-Methode den Dateinamen aufgelöst hat, prüft sie zuerst, ob das Modul in der Liste der nativen Module enthalten ist. Nehmen wir das http-Modul als Beispiel: Selbst wenn eine Datei http/http.js/http.node/http.json im Verzeichnis vorhanden ist, lädt require("http") nicht aus diesen Dateien, sondern aus dem nativen Modul.

Native Module haben ebenfalls einen Cache-Bereich, der ebenfalls zuerst aus dem Cache geladen wird. Wenn der Cache noch nicht geladen wurde, wird die Lademethode des nativen Moduls aufgerufen, um es zu laden und auszuführen.

Laden aus Dateien

Wenn ein Dateimodul nicht im Cache und kein natives Modul ist, parst Node.js die an die require-Methode übergebenen Parameter und lädt die eigentliche Datei aus dem Dateisystem. Die Details zum Wrapping und zur Kompilierung während des Ladevorgangs wurden im vorherigen Abschnitt vorgestellt. Hier beschreiben wir detailliert den Prozess des Auffindens von Dateimodulen, wobei es auch einige wissenswerte Details gibt.

Die require-Methode akzeptiert die folgenden Arten der Parameterübergabe:

  • http, fs, path usw., native Module.
  • ./mod oder ../mod, Dateimodule mit relativen Pfaden.
  • /pathtomodule/mod, Dateimodule mit absoluten Pfaden.
  • mod, ein Dateimodul, das kein natives Modul ist.

Ausführungsreihenfolge der unter Pfad Y ausgeführten require(X)-Anweisung:

1. 如果 X 是内置模块
   a. 返回内置模块
   b. 停止执行
2. 如果 X 以 '/' 开头
   a. 设置 Y 为文件根路径
3. 如果 X 以 './' oder  '/' or '../' 开头
   a. LOAD_AS_FILE(Y + X)
   b. LOAD_AS_DIRECTORY(Y + X)
4. LOAD_NODE_MODULES(X, dirname(Y))
5. 抛出异常 "not found"

LOAD_AS_FILE(X)
1. 如果 X 是一个文件, 将 X 作为 JavaScript 文本载入并停止执行。
2. 如果 X.js 是一个文件, 将 X.js 作为 JavaScript 文本载入并停止执行。
3. 如果 X.json 是一个文件, 解析 X.json 为 JavaScript 对象并停止执行。
4. 如果 X.node 是一个文件, 将 X.node 作为二进制插件载入并停止执行。

LOAD_INDEX(X)
1. 如果 X/index.js 是一个文件,  将 X/index.js 作为 JavaScript 文本载入并停止执行。
2. 如果 X/index.json 是一个文件, 解析 X/index.json 为 JavaScript 对象并停止执行。
3. 如果 X/index.node 是一个文件,  将 X/index.node 作为二进制插件载入并停止执行。

LOAD_AS_DIRECTORY(X)
1. 如果 X/package.json 是一个文件,
   a. 解析 X/package.json, 并查找 "main" 字段。
   b. let M = X + (json main 字段)
   c. LOAD_AS_FILE(M)
   d. LOAD_INDEX(M)
2. LOAD_INDEX(X)

LOAD_NODE_MODULES(X, START)
1. let DIRS=NODE_MODULES_PATHS(START)
2. for each DIR in DIRS:
   a. LOAD_AS_FILE(DIR/X)
   b. LOAD_AS_DIRECTORY(DIR/X)

NODE_MODULES_PATHS(START)
1. let PARTS = path split(START)
2. let I = count of PARTS - 1
3. let DIRS = []
4. while I >= 0,
   a. if PARTS[I] = "node_modules" CONTINUE
   b. DIR = path join(PARTS[0 .. I] + "node_modules")
   c. DIRS = DIRS + DIR
   d. let I = I - 1
5. return DIRS

Verwendung von exports und module.exports

Wenn Sie Eigenschaften oder Methoden nach außen verfügbar machen möchten, verwenden SieexportsDas ist alles. Um ein Objekt (ähnlich einer Klasse, das viele Eigenschaften und Methoden enthält) verfügbar zu machen, verwenden Siemodule.exports。

Weitere Erweiterungen