Fast jeder Node.js-Entwickler kann Ihnen sagen, was die Funktion require() macht, aber wie viele von uns wissen tatsächlich, wie sie funktioniert? Wir verwenden es täglich zum Laden von Bibliotheken und Modulen, aber sein Verhalten ist uns ein Rätsel.
Aus Neugier habe ich mich in den Kerncode von Node vertieft, um herauszufinden, was unter der Haube vor sich geht. Aber das ist keine einzelne Funktion. Ich habe module.js im Modulsystem des Knotens gefunden. Diese Datei enthält ein überraschend leistungsstarkes und relativ unbekanntes Kernmodul, das das Laden, Kompilieren und Zwischenspeichern jeder Datei steuert. Das Aufkommen von require() ist nur die Spitze des Eisbergs.
module.jsfunction Module(id, parent) { this.id = id; this.exports = {}; this.parent = parent; // ...
Module.js spielt innerhalb von Node.js hauptsächlich zwei Rollen. Erstens bietet es eine Grundlage für alle Node.js-Module. Jede Datei ist eine neue Instanz eines Basismoduls, die auch nach der Ausführung der Datei bestehen bleibt. Aus diesem Grund können wir Attribute an module.exports anhängen und sie bei Bedarf zurückgeben.
Die zweite Hauptaufgabe dieses Moduls besteht darin, den Modullademechanismus des Knotens zu verwalten. Die von uns verwendete unabhängige Operationsfunktion „require“ ist eigentlich ein abstraktes Konzept von module.require, das selbst nur eine einfache Kapselung der Funktion Module._load ist. Diese Lademethode übernimmt das eigentliche Laden jeder Datei und beginnt unsere Reise dorthin.
Module._load Module._load = function(request, parent, isMain) { // 1. Check Module._cache for the cached module. // 2. Create a new Module instance if cache is empty. // 3. Save it to the cache. // 4. Call module.load() with your the given filename. // This will call module.compile() after reading the file contents. // 5. If there was an error loading/parsing the file, // delete the bad module from the cache // 6. return module.exports };
Module._load ist für das Laden neuer Module und die Verwaltung von Modulcaches verantwortlich. Das Zwischenspeichern jedes geladenen Moduls reduziert die Anzahl redundanter Dateilesevorgänge und kann Ihre Anwendung erheblich beschleunigen. Darüber hinaus ermöglichen gemeinsam genutzte Modulinstanzen, dass Singleton-Funktionen von Modulen im Projektstatus verbleiben.
Wenn ein Modul nicht im Cache vorhanden ist, erstellt Module._load ein neues Basismodul dieser Datei. Anschließend weist es das Modul an, den Inhalt der neuen Dateien zu lesen, bevor es sie an module._compile sendet. [1]
Wenn Sie Schritt Nr. 6 oben bemerken, werden Sie sehen, dass module.exports an den Benutzer zurückgegeben wurde. Aus diesem Grund verwenden Sie beim Definieren einer zu verwendenden öffentlichen Schnittstelle exports und module.exports, da Module._load als nächstes den erforderlichen Inhalt zurückgibt. Ich bin überrascht, dass es hier nicht mehr Funktionen gibt, aber es wäre schön, wenn es welche gäbe.
module._compile Module.prototype._compile = function(content, filename) { // 1. Create the standalone require function that calls module.require. // 2. Attach other helper methods to require. // 3. Wraps the JS code in a function that provides our require, // module, etc. variables locally to the module scope. // 4. Run that function};
Hier geschieht die wahre Magie. Zunächst wird für dieses Modul eine spezielle eigenständige Require-Funktion erstellt. Dies ist eine Funktion, die wir alle brauchen und mit der wir vertraut sind. Die Funktion selbst ist nur ein Paket in Module.require. Sie enthält auch einige wenig bekannte Hilfsmethoden, die für uns einfach zu verwenden sind:
· require(): Laden eines externen Moduls
· require. Auflösung (): Lösen Sie einen Modulnamen in seinen absoluten Pfad auf
· require.main: Hauptmodul
· require.cache: alle zwischengespeicherten Module
· require.extensions: basierend auf seiner Erweiterung, für jedes A gültig Der Dateityp kann mit
kompiliert werden. Sobald require bereit ist, wird der gesamte geladene Quellcode in einer neuen Funktion gekapselt, die require, module, exports und alle anderen offengelegten Variablen als Parameter akzeptieren kann. Dies ist eine Funktion, die ausschließlich zur Kapselung des Moduls erstellt wurde, um Konflikte mit der Node.js-Umgebung zu verhindern.
(function (exports, require, module, __filename, __dirname) { // YOUR CODE INJECTED HERE! });
Die Module._compile-Methode wird synchron ausgeführt, sodass der Aufruf von Module._load nur bis zum Ende dieses Codes warten und module.exprts an den Benutzer zurückgeben kann.
Fazit
Wir haben also den gesamten Anforderungscode verstanden und haben ein vorläufiges Verständnis dafür, wie er funktioniert.
Wenn Sie das alles befolgt haben, sind Sie bereit für das letzte Geheimnis: require(‘module’). Richtig, das Modulsystem selbst kann über das Modulsystem geladen werden. Beginn. Das mag seltsam klingen, aber es ermöglicht dem Benutzerraum, mit dem Modulladesystem zu interagieren, ohne sich mit dem Kern von Node.j befassen zu müssen. Beliebte Module sind so aufgebaut. [2]
Wenn Sie mehr wissen möchten, überprüfen Sie bitte selbst den Quellcode von module.js. Es gibt genug Dinge, die Ihnen für eine Weile Kopfschmerzen bereiten. Der erste, der mir sagen kann, was NODE_MODULE_CONTEXTS ist und warum es hinzugefügt wird, kann Bonuspunkte bekommen :)
[1] Die module._compile-Methode wird nur zum Ausführen von JSON-Dateien verwendet, denen JSON übergeben werden muss . parse() analysiert und gibt
[2] Beide Module basieren jedoch auf privaten Modulmethoden wie Module._resolveLookupPaths und Module._findPath. Sie können sich das nicht viel besser vorstellen...
几乎所有的Node.js开发人员可以告诉你require()函数做什么,但我们又有多少人真正知道它是如何工作的?我们每天都使用它来加载库和模块,但它的行为,对于我们来说反而是一个谜。
出于好奇,我钻研了node的核心代码来找出在引擎下发生了什么事。但这并不是一个单一的功能,我在node的模块系统的找到了module.js。该文件包含一个令人惊讶的强大的且相对陌生的核心模块,控制每个文件的加载,编译和缓存。require() 它的横空出世,只是冰山的一角。
module.jsfunction Module(id, parent) { this.id = id; this.exports = {}; this.parent = parent; // ...
在module.js在Node.js内部主要承担两个角色。首先,它为所有的Node.js模块提供了一个基础。每个文件是基本模块new出的一个新实例,即使在该文件已经运行之后,仍然存在。这就是为什么我们能够性为module.exports附加属并在需要时返回它们。
该模块的第二大任务是处理node的模块加载机制。我们使用的独立操作的“require”函数实际上是一个抽象概念的module.require,这本身就是只是一个简单的关于Module._load功能的封装。此load方法处理每个文件的实际加载,并在那里开始我们的旅程。
Module._load Module._load = function(request, parent, isMain) { // 1. Check Module._cache for the cached module. // 2. Create a new Module instance if cache is empty. // 3. Save it to the cache. // 4. Call module.load() with your the given filename. // This will call module.compile() after reading the file contents. // 5. If there was an error loading/parsing the file, // delete the bad module from the cache // 6. return module.exports };
Module._load负责加载新的模块和管理模块的缓存。缓存加载的每个模块减少冗余文件的读取次数,并可以显著地加快您应用程序的速度。此外,共享模块实例允许单例特性的模块,保持在项目中的状态。
如果某个模块没有在缓存中存在,Module._load将创建该文件的一个新的基本模块。然后,它会告诉模块在将它们发送到module._compile之前阅读新文件的内容。[1]
如果您注意到上面的步骤#6,你会看到module.exports已被返回给用户。这就是为什么当你在定义公共接口使用时,你使用exports和module.exports,因为Module._load将接下来返回require的内容。我很惊讶,这里没有更多的功能,但如果有的话那更好。
module._compile Module.prototype._compile = function(content, filename) { // 1. Create the standalone require function that calls module.require. // 2. Attach other helper methods to require. // 3. Wraps the JS code in a function that provides our require, // module, etc. variables locally to the module scope. // 4. Run that function};
这是真正的奇迹发生的地方。首先,一个特殊的独立操作的require函数是为该模块创建的。这是我们需要的并且都熟悉的功能。而函数本身只是一个在Module.require的封装,它也包含了一些便于我们使用的鲜为人知的辅助方法:
· require():加载一个外部模块
· require.resolve():解析一个模块名到它的绝对路径
· require.main:主模块
· require.cache:所有缓存好的模块
· require.extensions:根据其扩展名,对于每个有效的文件类型可使用的编制方法
一旦require准备好了,整个加载的源代码就会被封装在一个新的函数里,可以接受require,module,exports和所有其他暴露的变量作为参数。这是一个仅仅为封装模块的而创建的函数,以便于在防止与Node.js的环境产生冲突。
(function (exports, require, module, __filename, __dirname) { // YOUR CODE INJECTED HERE! });
该Module._compile方法是同步执行的,所以对Module._load的调用只能等到这段代码运行结束,并将module.exprts返回给用户。
结论
因此,我们已经了解了require的全部代码,并已经初步了解它是如何工作的。
如果你已经按照这一切的方式做了,那么你已经为最后的秘密做好准备:require(‘module’)。这是正确的,该模块系统本身可以通过模块系统被加载。盗梦空间。这可能听起来很奇怪,但它可以让用户空间同模块加载系统互动起来,并不需要钻研Node.js核心。受欢迎的模块都像这样被建立。[2]
如果您想了解更多,请自己查看module.js源代码。还有很多东西足够你头痛一段时间了。第一个可以告诉我什么是NODE_MODULE_CONTEXTS“以及它为什么被添加的人可以得到加分奖励 :)
[1] module._compile方法只用于运行JavaScript文件。 JSON文件需通过JSON.parse() 解析并返回
[2]然而,这两个模块都建立在私有模块的方法,如Module._resolveLookupPaths和Module._findPath。你可以认为这并没有好多了…
Das obige ist der detaillierte Inhalt vonEntdecken Sie, wie die require()-Methode in Node.js funktioniert. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!