Tout ce que vous devez savoir sur 'module' et 'require' dans Node.js

Modules

Node.js traite chaque fichier JavaScript comme un module distinct.

Par exemple, si vous avez un fichier contenant du code et que ce fichier est nommé xyz.js, alors ce fichier est traité comme un module dans Node, et vous pouvez dire que vous avez créé un module nommé xyz.

Prenons un exemple pour mieux comprendre cela.

Vous avez un fichier nommé circle.jsqui comprend la logique de calcul de la surface et de la circonférence d'un cercle d'un rayon donné, comme indiqué ci-dessous:

circle.js

Vous pouvez appeler circle.jsfichier un module nommé circle.

Vous vous demandez peut-être pourquoi est-il nécessaire d'avoir plusieurs modules? Vous auriez pu simplement écrire tout le code dans un seul module. Eh bien, il est très important d'écrire du code modulaire. Par modulaire, je veux dire que votre code doit être indépendant et doit être faiblement couplé. Imaginez qu'il existe une grande application et que vous avez tout votre code écrit en un seul endroit, un seul fichier. Trop salissant, non?

Comment fonctionne le code écrit à l'intérieur d'un module?

Avant d'exécuter le code écrit à l'intérieur d'un module, Node prend tout le code et l'enferme dans un wrapper de fonction. La syntaxe de ce wrapper de fonction est:

Le wrapper de fonction du circlemodule ressemblera à celui donné ci-dessous:

Vous pouvez voir qu'il existe un wrapper de fonction au niveau racine englobant tout le code écrit à l'intérieur du circlemodule.

Le code entier écrit à l'intérieur d'un module est privé pour le module, sauf indication contraire explicite (exportée).

C'est l'avantage le plus significatif d'avoir des modules dans Node.js. Même si vous définissez une variable globale dans un module à l'aide de var, letou des constmots-clés, les variables sont étendues localement au module plutôt que globalement. Cela se produit car chaque module a son propre wrapper de fonction et le code écrit à l'intérieur d'une fonction est local à cette fonction et n'est pas accessible en dehors de cette fonction.

Imaginez qu'il ya deux modules - A et B . Le code écrit à l' intérieur du module A est enfermé dans la fonction enveloppe correspondant au module A . Chose semblable se produit avec le code écrit à l' intérieur du module B . Étant donné que le code relatif aux deux modules est inclus dans des fonctions différentes, ces fonctions ne pourront pas accéder au code de l'autre. (Rappelez-vous que chaque fonction en JavaScript a sa propre portée locale?) C'est la raison pour laquelle le module A ne peut pas accéder au code écrit dans le module B et vice-versa.

Les cinq paramètres - exports, require, module, __filename, __dirnamesont disponibles à l' intérieur de chaque module dans le nœud. Bien que ces paramètres soient globaux au code dans un module, ils sont cependant locaux au module (à cause du wrapper de fonction comme expliqué ci-dessus). Ces paramètres fournissent des informations précieuses liées à un module.

Revoyons le circlemodule, que vous avez examiné plus tôt. Il y a trois constructions définies dans ce module - une variable constante PI, une fonction nommée calculateAreaet une autre fonction nommée calculateCircumference. Un point important à garder à l'esprit est que toutes ces constructions sont privées pour le circlemodule par défaut. Cela signifie que vous ne pouvez pas utiliser ces constructions dans un autre module, sauf indication explicite.

Donc, la question qui se pose maintenant est de savoir comment spécifier quelque chose dans un module qui peut être utilisé par un autre module? C'est à ce moment que les paramètres module& requiredu wrapper de fonction sont utiles. Discutons de ces deux paramètres dans cet article.

module

Le moduleparamètre (plutôt un mot-clé dans un module dans Node) fait référence à l'objet représentant le module courant . exportsest une clé de l' moduleobjet dont la valeur correspondante est un objet. La valeur par défaut de l' module.exportsobjet est {}(objet vide). Vous pouvez vérifier cela en enregistrant la valeur du modulemot - clé dans n'importe quel module. Vérifions quelle est la valeur du moduleparamètre à l'intérieur du circlemodule.

circle.js

Notez qu'il y a une console.log(module);instruction à la fin du code dans le fichier ci-dessus. Lorsque vous voyez la sortie, il enregistrera l' moduleobjet, qui a une clé nommée exportset la valeur correspondant à cette clé est {}(un objet vide).

Maintenant, que fait l' module.exportsobjet? Eh bien, il est utilisé pour définir des éléments qui peuvent être exportés par un module. Tout ce qui est exporté d'un module peut, à son tour, être mis à la disposition d'autres modules. Exporter quelque chose est assez simple. Il vous suffit de l'ajouter à l' module.exportsobjet. Il existe trois façons d'ajouter quelque chose à l' module.exportsobjet à exporter. Discutons de ces méthodes une par une.

Méthode 1:

(Définition de constructions, puis utilisation de plusieurs module.exportsinstructions pour ajouter des propriétés)

Dans la première méthode, vous définissez d'abord les constructions, puis vous utilisez plusieurs instructions module.exports où chaque instruction est utilisée pour exporter quelque chose à partir d'un module. Regardons cette méthode en action et voyons comment vous pouvez exporter les deux fonctions définies dans le circlemodule.

circle.js

Comme je vous l'ai dit plus tôt, moduleest un objet ayant la clé nommée exportset cette clé ( module.exports), à son tour, se compose d'un autre objet. Maintenant, si vous remarquez le code donné ci-dessus, tout ce que vous faites est d'ajouter de nouvelles propriétés (paires clé-valeur) à l' module.exportsobjet.

La première propriété a la clé calculateArea(défini à la ligne 19)et la valeur écrite à droite de l'opérateur d'affectation est la fonction définie avec le nom calculateArea(sur la ligne 9).

La deuxième propriété (définie à la ligne 20) a la clé calculateCircumferenceet la valeur est la fonction définie avec le nom calculateCircumference(à la ligne 16).

Ainsi, vous avez attribué deux propriétés (paires clé-valeur) à l' module.exportsobjet.

N'oublions pas non plus que vous avez utilisé ici la notation par points. Vous pouvez également utiliser la notation entre crochets pour affecter les propriétés à l' module.exportsobjet et ajouter les fonctions - calculateAreaet calculateCircumferenceen spécifiant les touches suivant la notation entre crochets. Ainsi, vous pouvez écrire les deux lignes suivantes pour ajouter des propriétés à l' module.exportsobjet en utilisant la notation entre crochets tout en remplaçant les deux dernières lignes (en utilisant la notation par points) dans le code donné ci-dessus:

// exporting stuff by adding to module.exports object using the bracket notation
module.exports['calculateArea'] = calculateArea;module.exports['calculateCircumference'] = calculateCircumference; 

Essayons maintenant de consigner la valeur de l' module.exportsobjet après avoir ajouté les propriétés. Notez que l'instruction suivante est ajoutée à la fin du code dans le fichier ci-dessous:

// logging the contents of module.exports object after adding properties to it
console.log(module.exports);

circle.js

Vérifions la sortie de ce code et voyons si tout fonctionne correctement. Pour ce faire, enregistrez votre code et exécutez la commande suivante dans votre terminal :

node circle

Production:

{ calculateArea: [Function: calculateArea], calculateCircumference: [Function: calculateCircumference] }

Les constructions - calculateAreaet calculateCircumference, ajoutées à l' module.exportsobjet, sont enregistrées. Ainsi, vous avez ajouté avec succès les deux propriétés dans l' module.exportsobjet afin que les fonctions - calculateAreaet calculateCircumferencepuissent être exportées du circlemodule vers un autre module.

Dans cette méthode, vous avez d'abord défini toutes les constructions, puis utilisé plusieurs instructions module.exports où chaque instruction est utilisée pour ajouter une propriété à l' module.exportsobjet.

Méthode 2:

(Définir des constructions puis utiliser une seule module.exportsinstruction pour ajouter des propriétés)

Une autre méthode consiste à définir d'abord toutes les constructions (comme vous l'avez fait dans la méthode précédente) mais à utiliser une seule module.exportsinstruction pour toutes les exporter. Cette méthode est similaire à la syntaxe de la notation littérale d'objet où vous ajoutez toutes les propriétés à un objet à la fois.

Ici, vous avez utilisé la notation littérale d'objet et ajouté les deux fonctions - calculateArea et calculateCircumference(toutes à la fois) à l' module.exportsobjet en écrivant une seule instruction module.exports .

Si vous vérifiez la sortie de ce code, vous obtiendrez le même résultat que vous avez obtenu précédemment en utilisant la méthode 1.

Méthode 3:

(Ajout de propriétés à l' module.exportsobjet lors de la définition des constructions)

Dans cette méthode, vous pouvez ajouter les constructions à l' module.exportsobjet tout en les définissant. Voyons comment cette méthode peut être adoptée dans notre circlemodule.

Dans le code donné ci-dessus, vous pouvez voir que les fonctions du module sont ajoutées à l' module.exportsobjet lors de leur définition. Regardons comment cela fonctionne. Vous ajoutez une clé calculateAreaà l' module.exportsobjet et la valeur correspondant à cette clé est la définition de la fonction.

Notez que la fonction n'a plus de nom et est une fonction anonyme qui est simplement traitée comme une valeur pour une clé d'un objet. Ainsi, cette fonction ne peut pas être référencée dans le circlemodule et vous ne pouvez pas appeler cette fonction à l'intérieur de ce module en écrivant l'instruction suivante:

calculateArea(8);

Si vous essayez d'exécuter l'énoncé ci - dessus, vous obtiendrez un ReferenceErrordéclarant calculateArea is not defined.

Maintenant que vous avez appris comment spécifier ce qui doit être exporté à partir d'un module, comment pensez-vous que l'autre module pourra utiliser les éléments exportés? Vous devez importer le module dans un autre module afin de pouvoir utiliser les éléments exportés du premier dans le second. C'est à ce moment que nous devons discuter d'un autre paramètre nommé require.

exiger

requiremot-clé fait référence à une fonction qui est utilisée pour importer toutes les constructions exportées à l'aide de l' module.exportsobjet d'un autre module. Si vous avez un module x dans lequel vous exportez des constructions en utilisant l' module.exportsobjet et que vous voulez importer ces constructions exportées dans le module y , vous devez alors exiger le module x dans le module y en utilisant la requirefonction. La valeur retournée par la requirefonction dans le module y est égale à l' module.exportsobjet dans le module x .

Comprenons cela en utilisant l'exemple dont nous avons discuté précédemment. Vous disposez déjà du circlemodule à partir duquel vous exportez les fonctions calculateAreaet calculateCircumference. Voyons maintenant comment vous pouvez utiliser la requirefonction pour importer les éléments exportés dans un autre module.

Créons d'abord un nouveau fichier dans lequel vous utiliserez le code exporté du circlemodule. Nommons ce fichier app.jset vous pourrez l'appeler appmodule.

L'objectif est d'importer dans le appmodule tout le code exporté depuis le circlemodule. Alors, comment pouvez-vous inclure votre code écrit dans un module dans un autre module?

Considérez la syntaxe de la requirefonction donnée ci-dessous:

const variableToHoldExportedStuff = require('idOrPathOfModule');

La requirefonction prend un argument qui peut être un ID ou un chemin. L'ID fait référence à l'id (ou au nom) du module requis. Vous devez fournir l'ID comme argument lorsque vous utilisez les modules tiers ou les modules principaux fournis par Node Package Manager. D'autre part, lorsque vous avez défini des modules personnalisés, vous devez fournir le chemin du module comme argument. Vous pouvez en savoir plus sur la fonction require à partir de ce lien.

Étant donné que vous avez déjà défini un module personnalisé nommé circle, vous fournirez le chemin comme argument de la requirefonction.

app.js

Si vous remarquez clairement, le point au début du chemin signifie qu'il s'agit d'un chemin relatif et que les modules appet circlesont stockés sur le même chemin.

Connectons à la console la circlevariable, qui contient le résultat retourné par la requirefonction. Voyons ce que contient cette variable.

app.js

Vérifiez la sortie en enregistrant tout votre code et en exécutant la commande suivante dans votre terminal (cette dernière n'est pas requise si le nodemonpackage est installé):

node app

Production:

{ calculateArea: [Function: calculateArea],calculateCircumference: [Function: calculateCircumference] }

Comme vous pouvez le voir, la requirefonction retourne un objet dont les clés sont les noms des variables / fonctions qui ont été exportées depuis le module requis ( circle). En bref, la requirefonction renvoie l' module.exportsobjet.

Accédons maintenant aux fonctions importées du circlemodule.

app.js

Production:

Area = 200.96, Circumference = 50.24

Que pensez-vous qu'il se passera si j'essaie d'accéder à la variable nommée PIdéfinie dans le circlemodule à l'intérieur du appmodule?

app.js

Production:

Area = 200.96, Circumference = 50.24pi = undefined

Pouvez - vous comprendre pourquoi piest undefined? Eh bien, c'est parce que la variable PIn'est pas exportée du circlemodule. Rappelez-vous le point où je vous ai dit que vous ne pouvez pas accéder au code écrit à l'intérieur d'un module dans un autre module car tout le code écrit à l'intérieur d'un module lui est privé à moins d'être exporté? Ici, vous essayez d'accéder à quelque chose qui n'a pas été exporté du circlemodule et qui lui est privé.

Alors, vous vous demandez peut-être pourquoi vous n'avez pas obtenu de fichier ReferenceError. Cela est dû au fait que vous essayez d'accéder à une clé nommée PIà l'intérieur de l' module.exportsobjet renvoyé par la requirefonction. Vous savez également que la clé nommée PIn'existe pas dans l' module.exportsobjet.

Notez que lorsque vous essayez d'accéder à une clé inexistante dans un objet, vous obtenez le résultat sous la forme undefined. C'est la raison pour laquelle vous obtenez PIcomme undefinedau lieu d'obtenir un fichier ReferenceError.

Maintenant, exportons la variable PIdu circlemodule et voyons si la réponse change.

circle.js

Notez qu'ici, vous n'utilisez pas le nom de la variable PIcomme clé de la propriété ajoutée à l' module.exportsobjet. Vous utilisez plutôt un autre nom, qui est lifeOfPi.

C'est une chose intéressante à noter. Lorsque vous exportez une construction de codage, vous pouvez donner n'importe quel nom à la clé lors de l'ajout d'une propriété ajoutée à l' module.exportsobjet. Il n'est pas obligatoire d'utiliser le même nom que le nom que vous avez utilisé lors de la définition de la construction. En effet, vous pouvez utiliser n'importe quel identifiant valide comme clé dans un objet JavaScript. Ainsi, sur le côté gauche de l'opérateur d'affectation, vous pouvez utiliser n'importe quel identifiant valide, mais sur le côté droit de l'opérateur d'affectation, vous devez fournir une valeur qui est définie comme une construction dans la portée du module courant (comme vous 'ai défini les variables et les fonctions dans le module' cercle ').

Un point important à noter est que lors de l'importation de quelque chose d'un autre module dans le module actuel, vous devez utiliser la même clé que vous avez utilisée lors de son exportation.

app.js

Parce que vous avez utilisé la clé lifeOfPi, vous devez utiliser la même clé pour accéder à la variable PIdéfinie dans le circlemodule, comme cela est fait dans le code ci-dessus.

Production:

Area = 200.96, Circumference = 50.24pi = 3.14

Que pensez-vous qu'il se passera si vous utilisez le nom de la variable au lieu d'utiliser la clé qui a été utilisée lors de l'exportation? En bref, essayons d'accéder à PI(nom de la variable) au lieu de lifeOfPi(clé utilisée lors de l'exportation PI).

app.js

Production:

Area = 200.96, Circumference = 50.24pi = undefined

Cela se produit parce que l' module.exportsobjet ne connaît plus la variable PI. Il connaît simplement les clés qui lui sont ajoutées. Comme la clé utilisée pour exporter la variable PIest lifeOfPi, cette dernière ne peut être utilisée que pour accéder à la première.

TL; DR

  • Chaque fichier dans Node.js est appelé module .
  • Avant d'exécuter le code écrit dans un module, Node.js prend tout le code écrit à l'intérieur du module et le convertit en un wrapper de fonction, qui a la syntaxe suivante:
(function(exports, require, module, __filename, __dirname) { // entire module code lives in here});
  • L'encapsuleur de fonctions garantit que tout le code écrit à l'intérieur d'un module lui est privé, sauf indication contraire explicite (exporté). Les paramètres exports, require, module, __filenameet __dirnameagir comme les variables globales à l'ensemble du code dans un module. Puisque chaque module a son propre wrapper de fonction, le code écrit à l'intérieur d'un wrapper de fonction devient local pour ce wrapper de fonction (module de lecture) et n'est pas accessible dans un autre wrapper de fonction (module de lecture).
  • modulemot-clé fait référence à l'objet représentant le module courant. L' moduleobjet a une clé nommée exports. module.exportsest un autre objet utilisé pour définir ce qui peut être exporté par un module et mis à disposition d'autres modules. En bref, si un module veut exporter quelque chose, il doit être ajouté à l' module.exportsobjet.
  • La valeur par défaut de l' module.exportsobjet est {}.
  • Il existe trois méthodes dans lesquelles vous pouvez exporter quelque chose à partir d'un module ou ajouter quelque chose à l' module.exportsobjet:

    1. Définissez d'abord toutes les constructions, puis utilisez plusieurs module.exportsinstructions où chaque instruction est utilisée pour exporter une construction.

    2. Définissez d'abord toutes les constructions, puis utilisez une seule module.exportsinstruction pour exporter toutes les constructions en même temps en suivant la notation littérale d'objet.

    3. Ajoutez des constructions à l' module.exportsobjet tout en les définissant.

  • requiremot-clé fait référence à une fonction qui est utilisée pour importer toutes les variables et fonctions exportées à l'aide de l' module.exportsobjet d'un autre module. En bref, si un fichier veut importer quelque chose, il doit le déclarer en utilisant la syntaxe suivante:
require('idOrPathOfModule');
  • Lors de l'exportation de quelque chose à partir d'un module, vous pouvez utiliser n'importe quel identifiant valide. Il n'est pas obligatoire de donner le nom exact de la variable / fonction comme clé de la propriété ajoutée à l' module.exportsobjet. Assurez-vous simplement que vous utilisez la même clé pour accéder à quelque chose que vous avez utilisé lors de l'exportation.