Presque tous les développeurs Node.js peuvent vous dire ce que fait la fonction `` require () ', mais combien d'entre nous savent vraiment comment cela fonctionne? Nous l'utilisons tous les jours pour charger des bibliothèques et des modules, mais son comportement est un mystère pour nous.
Par curiosité, j'ai plongé dans le code de nœud de base pour savoir ce qui s'est passé sous le moteur. Mais ce n'est pas une seule fonction. J'ai trouvé module.js dans le système de module de nœud. Le 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. «Besoin ()», son émergence n'est que la pointe de l'iceberg.
module.js
La copie de code est la suivante:
Module de fonction (id, parent) {
this.id = id;
this.exports = {};
this.parent = parent;
// ...
Dans module.js, il joue principalement deux rôles à l'intérieur de Node.js. Tout d'abord, il fournit une base pour tous les modules Node.js. Chaque fichier est une nouvelle instance du module de base nouveau, et il existe toujours même après l'exécution du fichier. C'est pourquoi nous pouvons attacher les propriétés à module.exports et les retourner en cas de besoin.
La deuxième tâche principale de ce module est de gérer le mécanisme de chargement du module du nœud. L'opération indépendante de la fonction "requise" que nous utilisons est en fait un module de concept abstrait. Cette méthode de chargement gère le chargement réel de chaque fichier et démarre notre voyage là-bas.
Module._load
La copie de code est la suivante:
Module._load = fonction (demande, parent, ismain) {
// 1. Vérifiez le module._cache pour le module mis en cache.
// 2. Créez une nouvelle instance de module si le cache est vide.
// 3. Enregistrez-le dans le cache.
// 4. Appelez le module.load () avec votre nom de fichier donné.
// Cela appellera module.compile () après avoir lu le contenu du fichier.
// 5. S'il y avait une erreur de chargement / d'analyse du fichier,
// Supprimer le mauvais module du cache
// 6. Return module.exports
};
Module._load est responsable du chargement de nouveaux modules et de la gestion des caches du module. Le cache chargé de chaque module réduit le nombre de lectures de fichiers redondants et peut accélérer considérablement votre application. De plus, l'instance de module partagé permet aux modules avec les caractéristiques singleton 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 pour le fichier. Il dira ensuite au module de lire le contenu du nouveau fichier 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 l'utilisation de l'interface publique, vous utilisez des exportations et module.exports, car module._load renvoie ensuite le contenu de l'exigence. J'ai été surpris qu'il n'y ait plus de fonctionnalités ici, mais ce serait mieux s'il y en avait.
module._compile
La copie de code est la suivante:
Module.prototype._compile = fonction (contenu, nom de fichier) {
// 1. Créer la fonction autonome nécessite une fonction qui appelle module.require.
// 2. Attachez les autres méthodes d'assistance à exiger.
// 3. Enveloppe le code JS dans une fonction qui fournit notre exigence,
// Module, etc. Variables localement à la portée du module.
// 4. Exécutez cette fonction
};
・ C'est là que se produit le vrai miracle. Tout d'abord, une fonction opérationnelle spéciale et indépendante est créée pour le module. Il s'agit d'une fonctionnalité dont nous avons besoin et que nous connaissons tous. La fonction elle-même est juste une encapsulation dans le module.
・ Require (): Chargez un module externe
・ Require.resolve (): analyser un nom de module sur son chemin absolu
・ Exiger.Main: module principal
・ Exiger.cache: tous les modules mis en cache
・ ・ Exiger.Extensions: une méthode de compilation qui peut être utilisée pour chaque type de fichier valide en fonction de son extension
Une fois que l'exigence est prête, l'ensemble du code source chargé sera encapsulé dans une nouvelle fonction, ce qui lui permet d'accepter l'exigence, le module, les exportations et toutes les autres variables exposées sous forme de paramètres. Il s'agit d'une fonction créée juste pour encapsuler des modules pour éviter les conflits avec l'environnement Node.js.
La copie de code est la suivante:
(fonction (exportations, require, module, __filename, __dirname) {
// Votre code injecté ici!
});
La méthode module._compile est exécutée de manière synchrone, de sorte que l'appel à module._load ne peut qu'attendre que le code soit l'exécution se termine et renvoie module.exprts à l'utilisateur.
en conclusion
Nous avons donc déjà compris le code complet des exigences et avons une compréhension préliminaire de son fonctionnement.
Si vous l'avez fait tout le long, vous êtes prêt pour le dernier secret: require («module»). Ceci est correct, le système de module lui-même peut être chargé via le système de module. Création. Cela peut sembler étrange, mais cela permet à l'espace utilisateur d'interagir avec les systèmes de chargement des modules sans plonger dans le noyau Node.js. Les modules populaires sont tous construits comme ça. [2]
Si vous voulez en savoir plus, veuillez consulter vous-même le code source module.js. Il y a encore beaucoup de choses qui seront suffisantes pour que vous ayez mal à la tête pendant un certain temps. Le premier peut me dire ce que Node_Module_Contexts est "et pourquoi il est ajouté peut obtenir des points bonus :)
[1] La méthode module._compile n'est utilisée que pour exécuter des fichiers JavaScript. Le fichier JSON doit être analysé via JSON.Parse () et retourné
[2] Cependant, les deux modules sont construits sur des méthodes de module privées telles que module._resolvelookuppaths et module._findpath. Vous pouvez penser que ce n'est pas beaucoup mieux ...