Comment gérer les dépendances cycliques dans Node.js

J’ai travaillé avec nodejs récemment et je m’excuse si c’est une question évidente. Je veux à peu près le code suivant:

a.js (le fichier principal est exécuté avec le noeud)

var ClassB = require("./b"); var ClassA = function() { this.thing = new ClassB(); this.property = 5; } var a = new ClassA(); module.exports = a; 

b.js

 var a = require("./a"); var ClassB = function() { } ClassB.prototype.doSomethingLater() { util.log(a.property); } module.exports = ClassB; 

Mon problème semble être que je ne peux pas accéder à l’instance de ClassA depuis une instance de ClassB.

Existe-t-il un moyen correct / meilleur de structurer les modules pour obtenir ce que je veux? Existe-t-il un meilleur moyen de partager les variables entre les modules?

Bien que node.js permette les dépendances circulaires, comme vous avez pu le constater, il peut être assez compliqué et il est probablement préférable de restructurer votre code pour ne pas en avoir besoin. Peut-être créer une troisième classe qui utilise les deux autres pour accomplir ce dont vous avez besoin.

Essayez de définir les propriétés sur module.exports , au lieu de le remplacer complètement. Par exemple, module.exports.instance = new ClassA() dans a.js , module.exports.ClassB = ClassB dans b.js Lorsque vous établissez des dépendances de module circulaires, le module requirejs obtient une référence à un module.exports incomplet à partir du module requirejs, auquel vous pouvez append d’autres propriétés, mais lorsque vous définissez l’intégralité de module.exports , vous créez un nouvel object. auquel le module requirejs n’a aucun moyen d’accéder.

[EDIT] ce n’est pas 2015 et la plupart des bibliothèques (c’est-à-dire express) ont fait des mises à jour avec de meilleurs modèles, donc les dépendances circulaires ne sont plus nécessaires. Je recommande simplement de ne pas les utiliser .


Je sais que je suis en train de trouver une vieille réponse ici. Le problème ici est que module.exports est défini après que vous ayez besoin de ClassB. (que montre le lien de JohnnyHK) Les dépendances circulaires fonctionnent très bien dans Node, elles sont simplement définies de manière synchrone. Lorsqu’ils sont utilisés correctement, ils résolvent en réalité un grand nombre de problèmes de nœuds courants (comme accéder à l’ app express.js à partir d’autres fichiers).

Assurez-vous simplement que vos exportations nécessaires sont définies avant de demander un fichier avec une dépendance circulaire.

Cela va casser:

 var ClassA = function(){}; var ClassB = require('classB'); //will require ClassA, which has no exports yet module.exports = ClassA; 

Cela fonctionnera:

 var ClassA = module.exports = function(){}; var ClassB = require('classB'); 

J’utilise ce modèle tout le temps pour accéder à l’ app express.js dans d’autres fichiers:

 var express = require('express'); var app = module.exports = express(); // load in other dependencies, which can now require this file and use app 

Parfois, il est vraiment artificiel d’introduire une troisième classe (comme le conseille JohnnyHK), donc en plus de Ianzz: Si vous voulez remplacer module.exports, par exemple si vous créez une classe (comme le fichier b.js dans l’exemple ci-dessus), cela est également possible, assurez-vous simplement que dans le fichier qui lance la circulaire, l’instruction ‘module.exports = …’ se produit avant l’instruction require.

a.js (le fichier principal est exécuté avec le noeud)

 var ClassB = require("./b"); var ClassA = function() { this.thing = new ClassB(); this.property = 5; } var a = new ClassA(); module.exports = a; 

b.js

 var ClassB = function() { } ClassB.prototype.doSomethingLater() { util.log(a.property); } module.exports = ClassB; var a = require("./a"); // < ------ this is the only necessary change 

La solution consiste à «déclarer à l’avance» votre object d’exportation avant de demander un autre contrôleur. Donc, si vous structurez tous vos modules comme ceci et que vous ne rencontrerez aucun problème comme celui-ci:

 // Module exports forward declaration: module.exports = { }; // Controllers: var other_module = require('./other_module'); // Functions: var foo = function () { }; // Module exports injects: module.exports.foo = foo; 

Une solution nécessitant un minimum de modifications est l’extension de module.exports au lieu de la remplacer.

a.js – point d’entrée et module d’application utilisant la méthode do de b.js *

 _ = require('underscore'); //underscore provides extend() for shallow extend b = require('./b'); //module `a` uses module `b` _.extend(module.exports, { do: function () { console.log('doing a'); } }); b.do();//call `b.do()` which in turn will circularly call `a.do()` 

b.js – module qui utilise la méthode do de a.js

 _ = require('underscore'); a = require('./a'); _.extend(module.exports, { do: function(){ console.log('doing b'); a.do();//Call `b.do()` from `a.do()` when `a` just initalized } }) 

Cela fonctionnera et produira:

 doing b doing a 

Bien que ce code ne fonctionnera pas:

a.js

 b = require('./b'); module.exports = { do: function () { console.log('doing a'); } }; b.do(); 

b.js

 a = require('./a'); module.exports = { do: function () { console.log('doing b'); } }; a.do(); 

Sortie:

 node a.js b.js:7 a.do(); ^ TypeError: a.do is not a function 

Qu’en est-il des fainéants nécessitant uniquement lorsque vous en avez besoin? Donc, votre b.js se présente comme suit

 var ClassB = function() { } ClassB.prototype.doSomethingLater() { var a = require("./a"); //a.js has finished by now util.log(a.property); } module.exports = ClassB; 

Bien sûr, il est recommandé de mettre toutes les déclarations obligatoires en haut du fichier. Mais il y a des occasions où je me pardonne de choisir quelque chose d’un module qui n’est pas lié par ailleurs. Appelez cela un hack, mais parfois c’est mieux que d’introduire une dépendance supplémentaire, d’append un module supplémentaire ou d’append de nouvelles structures (EventEmitter, etc.)

Vous pouvez résoudre ce problème facilement: exportez simplement vos données avant d’avoir besoin d’autre chose dans les modules où vous utilisez module.exports:

classA.js

 class ClassA { constructor(){ ClassB.someMethod(); ClassB.anotherMethod(); }; static someMethod () { console.log( 'Class A Doing someMethod' ); }; static anotherMethod () { console.log( 'Class A Doing anotherMethod' ); }; }; module.exports = ClassA; var ClassB = require( "./classB.js" ); let classX = new ClassA(); 

classB.js

 class ClassB { constructor(){ ClassA.someMethod(); ClassA.anotherMethod(); }; static someMethod () { console.log( 'Class B Doing someMethod' ); }; static anotherMethod () { console.log( 'Class A Doing anotherMethod' ); }; }; module.exports = ClassB; var ClassA = require( "./classA.js" ); let classX = new ClassB(); 

Semblable aux réponses de lanzz et setect, j’ai utilisé le modèle suivant:

 module.exports = Object.assign(module.exports, { firstMember: ___, secondMember: ___, }); 

Object.assign() copie les membres dans l’object exports qui a déjà été atsortingbué à d’autres modules.

L’affectation = est logiquement redondante, car elle ne fait que paramétrer module.exports , mais je l’utilise car cela aide mon IDE (WebStorm) à reconnaître que firstMember est une propriété de ce module, donc “Aller à -> Déclaration” (Cmd-B) et d’autres outils fonctionneront à partir d’autres fichiers.

Ce modèle n’est pas très joli, donc je ne l’utilise que lorsqu’un problème de dépendance cyclique doit être résolu.

Une autre méthode que j’ai vu des gens est d’exporter à la première ligne et de l’enregistrer en tant que variable locale comme ceci:

 let self = module.exports = {}; const a = require('./a'); // Exporting the necessary functions self.func = function() { ... } 

J’ai tendance à utiliser cette méthode, connaissez-vous des inconvénients?

En fait, j’ai fini par exiger ma dépendance avec

  var a = null; process.nextTick(()=>a=require("./a")); //Circular reference! 

Ce n’est pas beau, mais ça marche. Il est plus compréhensible et plus honnête que de changer b.js (par exemple, uniquement en augmentant modules.export), ce qui est parfait autrement.

pour votre problème, vous pouvez utiliser les déclarations de fonction.

class-b.js:

 var ClassA = require('./class-a') module.exports = ClassB function ClassB() { } 

class-a.js:

 var classB = require('./class-b') module.exports = ClassA function ClassA() { }