Comment gérer les dépendances cycliques dans le noeud.js

j'ai travaillé avec nodejs ces derniers temps et je suis encore en train de me familiariser avec le système de modules alors toutes mes excuses si c'est une question évidente. Je veux un code à peu près comme ci-dessous:

A. js (le fichier principal s'exécute 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 à partir d'une instance de Classeb.

y a-t-il une façon correcte / meilleure de structurer les modules pour obtenir ce que je veux? Est-il une meilleure façon de partager des variables entre les modules?

113
demandé sur icktoofay 2012-06-03 13:42:20

12 réponses

pendant le noeud.js autorise les dépendances circulaires require , car vous avez trouvé qu'il peut être assez désordonné et vous êtes probablement mieux 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.

51
répondu JohnnyHK 2014-05-22 01:16:45

essayez de définir les propriétés sur module.exports , au lieu de le remplacer complètement. E. g., module.exports.instance = new ClassA() dans a.js , module.exports.ClassB = ClassB dans b.js . Lorsque vous faites des dépendances de module circulaire, le module requérant obtiendra une référence à un module.exports incomplet du module requis, que vous pouvez ajouter d'autres propriétés plus tard, mais lorsque vous définissez le module.exports entier , vous créez en fait un nouvel objet auquel le module requis n'a aucun moyen d'accéder.

141
répondu lanzz 2012-06-03 18:54:03

[EDIT] ce n'est pas 2015 et la plupart des bibliothèques (i.e. express) ont fait des mises à jour avec de meilleurs modèles de sorte que les dépendances circulaires ne sont plus nécessaires. Je recommande simplement de ne pas les utiliser .


Je sais que je cherche une vieille réponse... Le problème ici est que le module.les exportations sont définies après vous avez besoin de la Classeb. (que le lien de JohnnyHK montre) Les dépendances circulaires fonctionnent bien dans Node, ils sont juste définis de façon synchrone. Lorsqu'ils sont utilisés correctement, ils résolvent en fait beaucoup de problèmes de nœuds communs (comme l'accès à express.js app à partir d'autres fichiers)

assurez-vous simplement que vos exportations nécessaires sont définies avant vous avez besoin d'un fichier avec une dépendance circulaire.

Ce sera la rupture:

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

module.exports = ClassA;

Cela va fonctionner:

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

j'utilise ce modèle tout le temps pour accéder à l'express.js app dans les autres fichiers:

var express = require('express');
var app = module.exports = express();
// load in other dependencies, which can now require this file and use app
41
répondu Will Stern 2015-09-04 16:00:22

parfois il est vraiment artificiel d'introduire une troisième classe (comme JohnnyHK conseille), donc en plus de Ianzz: Si vous voulez remplacer le module.exportations, par exemple si vous créez une classe (comme le B. JS dans l'exemple ci-dessus), ceci est également possible, assurez-vous que dans le fichier qui démarre la circulaire require, le module'.exportation. = ..'déclaration se produit avant la déclaration require.

A. js (le fichier principal courir avec nœud)

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
27
répondu Corno 2012-12-31 06:26:17

la solution est de "transmettre déclarer" vos exportations objet avant d'exiger tout autre contrôleur. Donc si vous structurez tous vos modules comme ceci et que vous ne rencontrerez pas de problèmes comme cela:

// Module exports forward declaration:
module.exports = {

};

// Controllers:
var other_module = require('./other_module');

// Functions:
var foo = function () {

};

// Module exports injects:
module.exports.foo = foo;
10
répondu Nicolas Gramlich 2014-08-18 00:35:39

une solution qui nécessite un minimum de changement est d'étendre module.exports au lieu de l'annuler.

un.js - app point d'entrée et le module d'utiliser la méthode 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 utilisent la méthode do À partir 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 
    }
})

il fonctionnera et produira:

doing b
doing a

Alors 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
7
répondu setec 2016-08-12 21:19:24

Qu'en est-il paresseux exigeant seulement quand vous avez besoin? Donc ton B. js looks as follows

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 de bonne pratique de mettre toutes les déclarations require en haut du dossier. Mais là sont occasions, où je me pardonne de choisir quelque chose d'un autre module sans rapport. Appelez ça un piratage, mais parfois c'est mieux que d'introduire une nouvelle dépendance, ou d'ajouter un module supplémentaire ou d'ajouter de nouvelles structures (EventEmitter, etc)

4
répondu zevero 2017-04-17 13:36:28

une autre méthode que j'ai vu les gens faire est d'exporter à la première ligne et de le sauvegarder comme une 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, est-ce que vous en connaissez les inconvénients?

2
répondu Bence Gedai 2017-12-20 17:58:03

en fait j'ai fini par avoir besoin de ma dépendance avec

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

pas joli, mais ça marche. C'est plus compréhensible et honnête que de changer B. js (par exemple seulement modules de renforcement.export), qui autrement est parfait comme tel.

1
répondu zevero 2017-04-17 14:01:40

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

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

le Object.assign() copie les membres dans l'objet exports qui a déjà été donné à d'autres modules.

l'affectation = est logiquement redondante, puisqu'elle se fixe module.exports à elle-même, mais je l'utilise parce qu'elle aide mon IDE (WebStorm) à reconnaître que firstMember est une propriété de ce module, donc "Aller À -> Déclaration" (Cmd-B) et de l'outillage d'autres vont travailler à partir d'autres fichiers.

ce schéma n'est pas très joli, donc je ne l'utilise que lorsqu'un problème de dépendance cyclique doit être résolu.

1
répondu joeytwiddle 2017-05-05 04:40:51

vous pouvez résoudre cela facilement: il suffit d'exporter vos données avant que vous n'ayez besoin de quoi que ce soit d'autre dans les modules où vous utilisez module.exportations:

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();

classeb.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();
1
répondu Giuseppe Canale 2018-04-30 09:10:19

pour votre problème, vous pouvez utiliser des 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() {

}
-4
répondu Jonathan Ong 2016-09-02 02:40:31