Passer des arguments à require (lors du chargement du module)

114

Est-il possible de passer des arguments lors du chargement d'un module en utilisant require?

J'ai le module login.js qui fournit la fonctionnalité de connexion. Cela nécessite une connexion à la base de données et je souhaite que la même connexion à la base de données soit utilisée dans tous mes modules. Maintenant, j'exporte une fonction login.setDatabase (...) qui me permet de spécifier une connexion à une base de données, et cela fonctionne très bien. Mais je préfère passer la base de données et toute autre exigence lorsque je charge le module.

var db = ...
var login = require("./login.js")(db);

Je suis assez nouveau avec NodeJS et je développe généralement en utilisant Java et Spring Framework, donc oui ... c'est une injection de constructeur :) Est-il possible de faire quelque chose comme le code que j'ai fourni ci-dessus?

Andreas Selenwall
la source
Je recommanderais également de regarder les réponses à cette question. Comme indiqué dans ma réponse, un idiome courant est de transmettre l' appobjet aux modules requis.
David Weldon
Au lieu de faire tout cet argument en passant pour db, vous pouvez utiliser une implémentation singleton et appeler db.getInstance () si nécessaire.
wulfgarpro

Réponses:

237

Sur la base de vos commentaires dans cette réponse , je fais ce que vous essayez de faire comme ceci:

module.exports = function (app, db) {
    var module = {};

    module.auth = function (req, res) {
        // This will be available 'outside'.
        // Authy stuff that can be used outside...
    };

    // Other stuff...
    module.pickle = function(cucumber, herbs, vinegar) {
        // This will be available 'outside'.
        // Pickling stuff...
    };

    function jarThemPickles(pickle, jar) {
        // This will be NOT available 'outside'.
        // Pickling stuff...

        return pickleJar;
    };

    return module;
};

Je structure à peu près tous mes modules comme ça. Semble bien fonctionner pour moi.

flottantLomas
la source
L' appargument est-il nécessaire ou puis-je l'omettre? Mon module n'utilisera pas explicitement cet argument d'application, mais je ne sais pas s'il est requis par node.js pour quelque chose interne. Si c'est ok, ma déclaration de module ressemblerait à ceci:module.exports = function (db) {
Ulysses Alves
C'était spécifique à une application Express, donc certainement pas nécessaire.
FloatingLomas
@floatingLomas Le mécanisme de sérialisation en python s'appelle pickle: stackoverflow.com/questions/11218477/…
SadSeven
1
Alors, que se passe-t-il si vous référencez le module plusieurs fois? Le premier require(mymodule)(myargs)vous donnerait un module avec lequel travailler. Cependant, si vous le référencez ailleurs à partir d'un autre module ?? dans le système de base, il semble y avoir un cache impliqué, mais dans ce système, le cache renverrait la méthode du générateur nu lors des appels suivants à require(), et si vous vous passiez des arguments, require()(someargs)vous récupéreriez un module différent ... peut-être que je manque quelque chose
Tom H
2
@TomH Dans ce cas, vous voudrez le mettre en cache. Pour ce faire, vous auriez en var module;dehors de la fonction d'exportation, puis dès que vous entrez dans le, vous voudrez vérifier si moduleest déjà défini, et si c'est le cas, renvoyez-le simplement (et sinon, initialisez-le). Cela a-t-il du sens?
floatingLomas
37

Je ne sais pas si cela sera toujours utile aux gens, mais avec ES6, j'ai un moyen de le faire que je trouve propre et utile.

class MyClass { 
  constructor ( arg1, arg2, arg3 )
  myFunction1 () {...}
  myFunction2 () {...}
  myFunction3 () {...}
}

module.exports = ( arg1, arg2, arg3 ) => { return new MyClass( arg1,arg2,arg3 ) }

Et puis vous obtenez votre comportement attendu.

var MyClass = require('/MyClass.js')( arg1, arg2, arg3 )
vovkman
la source
32

Oui. Dans votre loginmodule, exportez simplement une seule fonction qui prend dbcomme argument. Par exemple:

module.exports = function(db) {
  ...
};
David Weldon
la source
1
J'ai essayé ça. Dans login.js, module.exports = function(app,db) { ... } module.exports.auth = function(req,res) { ... authentication stuff } il appelle la fonction "anonyme" et définit les variables appet db, mais, en faisant cela, mon application ne trouvera pas la authfonction. Qu'est-ce que je fais mal? Si je supprime la fonction anonyme, la authfonction redevient accessible.
Andreas Selenwall
Comme tout en javascript, exportsc'est un objet. Vous pouvez lui attribuer des propriétés (exportations multiples) ou lui attribuer une valeur. Il semble que vous ayez affecté des exportations à une fonction, mais que vous ayez ensuite attribué auth en tant que propriété de la fonction que vous avez affectée aux exportations. Vous devrez donc faire var auth = require("./login.js").authce qui n'est peut-être pas ce que vous vouliez. Si vous souhaitez utiliser le modèle de la question d'origine, il est probablement préférable de vous en tenir à une seule valeur d'exportation. Si cela n'a toujours pas de sens, je vous recommanderais de publier un résumé à mon avis.
David Weldon
1
Après avoir lu ceci à nouveau, il semble que vous ayez peut-être attribué en authtant que propriété de l' exportsobjet, puis plus tard dans le module que vous avez attribué exportsà une fonction (remplaçant ainsi l'affectation précédente). Si vous inversez l'ordre de l'affectation, vous devriez pouvoir accéder à la authfonction comme prévu. Encore une fois, c'est difficile à dire sans voir le code.
David Weldon
1
Merci beaucoup pour votre aide. Ce que je n'avais pas réalisé, c'était exactement ce que vous recherchiez avec require ('login'). Auth. Je pensais qu'il n'y avait aucune différence entre var login = require('login')et var login = require('login')(app), mais il y a une énorme différence, il n'y a pas de magie dans la fonction anonyme renvoyée, c'est juste une autre fonction / objet. Au lieu d'avoir a module.exports.auth, la fonction anomymous renvoie maintenant la fonction auth (entre autres), ie return { auth: authFunction, login: loginFunction}. Alors maintenant, ça marche. Merci.
Andreas Selenwall