Presque tous les développeurs Node.js peuvent vous dire ce que fait la fonction require(), mais combien d'entre nous savent réellement comment cela fonctionne ? Nous l'utilisons quotidiennement pour charger des bibliothèques et des modules, mais son comportement reste un mystère pour nous.
Par curiosité, j'ai fouillé dans le code principal de Node pour découvrir ce qui se passait sous le capot. Mais ce n'est pas une fonction unique. J'ai trouvé module.js dans le système de modules du nœud. Ce fichier contient un module de base étonnamment puissant et relativement inconnu qui contrôle le chargement, la compilation et la mise en cache de chaque fichier. L’émergence de require() n’est que la pointe de l’iceberg.
module.jsfunction Module(id, parent) { this.id = id; this.exports = {}; this.parent = parent; // ...
Module.js joue principalement deux rôles au sein de Node.js. Premièrement, il fournit une base pour tous les modules Node.js. Chaque fichier est une nouvelle instance d'un module de base qui persiste même après l'exécution du fichier. C'est pourquoi nous pouvons attacher des attributs à module.exports et les renvoyer en cas de besoin.
La deuxième tâche majeure de ce module est de gérer le mécanisme de chargement du module du nœud. La fonction d'opération indépendante "require" que nous utilisons est en fait un concept abstrait de module.require, qui lui-même n'est qu'une simple encapsulation de la fonction Module._load. Cette méthode de chargement gère le chargement réel de chaque fichier et commence notre voyage là-bas.
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 est responsable du chargement des nouveaux modules et de la gestion des caches de modules. La mise en cache de chaque module chargé réduit le nombre de lectures de fichiers redondantes et peut considérablement accélérer votre application. De plus, les instances de module partagées permettent aux fonctionnalités singleton des modules de rester dans l'état du projet.
Si un module n'existe pas dans le cache, Module._load créera un nouveau module de base de ce fichier. Il indique ensuite au module de lire le contenu des nouveaux fichiers avant de les envoyer à module._compile. [1]
Si vous remarquez l'étape 6 ci-dessus, vous verrez que module.exports a été renvoyé à l'utilisateur. C'est pourquoi lorsque vous définissez une interface publique à utiliser, vous utilisez exports et module.exports, car Module._load renverra ensuite le contenu requis. Je suis surpris qu'il n'y ait pas plus de fonctionnalités ici, mais ce serait bien s'il y en avait.
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};
C'est ici que la vraie magie opère. Tout d’abord, une fonction require autonome spéciale est créée pour ce module. Il s’agit d’une fonctionnalité dont nous avons tous besoin et que nous connaissons bien. La fonction elle-même n'est qu'un package dans Module.require. Elle contient également quelques méthodes auxiliaires peu connues et faciles à utiliser :
· require() : charge un module externe
· require. solve () : résout un nom de module en son chemin absolu
· require.main : module principal
· require.cache : tous les modules mis en cache
· require.extensions : en fonction de son extension, pour chaque A valide Le type de fichier peut être compilé en utilisant
Une fois require prêt, l'intégralité du code source chargé est encapsulé dans une nouvelle fonction qui peut accepter require, module, exports et toutes les autres variables exposées comme paramètres. Il s'agit d'une fonction créée uniquement pour encapsuler le module afin d'éviter les conflits avec l'environnement Node.js.
(function (exports, require, module, __filename, __dirname) { // YOUR CODE INJECTED HERE! });
La méthode Module._compile est exécutée de manière synchrone, donc l'appel à Module._load ne peut qu'attendre la fin de ce code et renvoyer module.exprts à l'utilisateur.
Conclusion
Nous avons donc compris l'intégralité du code de require et avons une compréhension préliminaire de son fonctionnement.
Si vous avez suivi tout cela, alors vous êtes prêt pour le dernier secret : require('module'). C'est vrai, le système de modules lui-même peut être chargé via le système de modules. Création. Cela peut sembler étrange, mais cela permet à l'espace utilisateur d'interagir avec le système de chargement des modules sans avoir à se plonger dans le noyau de Node.js. Les modules populaires sont construits comme ceci. [2]
Si vous souhaitez en savoir plus, veuillez vérifier vous-même le code source du module.js. Il y a suffisamment de choses pour vous donner mal à la tête pendant un moment. Le premier qui peut me dire ce qu'est NODE_MODULE_CONTEXTS et pourquoi il est ajouté peut obtenir des points bonus :)
[1] La méthode module._compile est uniquement utilisée pour exécuter des fichiers JavaScript qui doivent être transmis en JSON. . parse() analyse et renvoie
[2] Cependant, les deux modules sont construits sur des méthodes de module privées comme Module._resolveLookupPaths et Module._findPath Vous pouvez penser que cela n'est pas beaucoup mieux...
几乎所有的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。你可以认为这并没有好多了…
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!