Run*_*ble 148 module require cyclic-reference node.js cyclic-dependency
我最近一直在使用nodejs并且仍然掌握模块系统,所以如果这是一个显而易见的问题,请道歉.我想要的代码大致如下所示:
a.js(主节点与节点一起运行)
var ClassB = require("./b");
var ClassA = function() {
this.thing = new ClassB();
this.property = 5;
}
var a = new ClassA();
module.exports = a;
Run Code Online (Sandbox Code Playgroud)
b.js
var a = require("./a");
var ClassB = function() {
}
ClassB.prototype.doSomethingLater() {
util.log(a.property);
}
module.exports = ClassB;
Run Code Online (Sandbox Code Playgroud)
我的问题似乎是我无法从ClassB的实例中访问ClassA的实例.
是否有正确/更好的方法来构建模块以实现我想要的?有没有更好的方法在模块之间共享变量?
lan*_*nzz 170
尝试设置属性module.exports,而不是完全替换它.例如,module.exports.instance = new ClassA()in a.js,module.exports.ClassB = ClassBin b.js.当您创建循环模块依赖项时,需求模块将从所需模块获得对不完整的引用module.exports,您可以在后面添加其他属性,但是当您设置整个时module.exports,实际上创建了一个新对象,其中需求模块没有访问方式.
Joh*_*yHK 79
虽然node.js确实允许循环require依赖,但是你发现它可能非常混乱,你可能最好不要重构代码而不需要它.也许创建第三个类,使用其他两个来完成你需要的东西.
Wil*_*ern 48
[编辑]它不是2015年,大多数图书馆(即快递)都使用更好的模式进行更新,因此不再需要循环依赖.我建议不要使用它们.
app从其他文件访问express.js )
只需确保在需要具有循环依赖关系的文件之前定义了必要的导出.
这将打破:
var ClassA = function(){};
var ClassB = require('classB'); //will require ClassA, which has no exports yet
module.exports = ClassA;
Run Code Online (Sandbox Code Playgroud)
这将有效:
var ClassA = module.exports = function(){};
var ClassB = require('classB');
Run Code Online (Sandbox Code Playgroud)
我一直使用这种模式访问app其他文件中的express.js :
var express = require('express');
var app = module.exports = express();
// load in other dependencies, which can now require this file and use app
Run Code Online (Sandbox Code Playgroud)
Coe*_*oen 32
有时引入第三个类(如JohnnyHK建议的那样)是非常人为的,所以除了Ianzz之外:如果你确实想要替换module.exports,例如你正在创建一个类(比如b.js文件)上面的例子),这也是可能的,只要确保在启动循环要求的文件中,'module.exports = ...'语句在require语句之前发生.
a.js(主节点与节点一起运行)
var ClassB = require("./b");
var ClassA = function() {
this.thing = new ClassB();
this.property = 5;
}
var a = new ClassA();
module.exports = a;
Run Code Online (Sandbox Code Playgroud)
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
Run Code Online (Sandbox Code Playgroud)
Nic*_*ich 14
解决方案是在需要任何其他控制器之前"转发声明"您的导出对象.因此,如果您构建所有模块,那么您将不会遇到任何类似的问题:
// Module exports forward declaration:
module.exports = {
};
// Controllers:
var other_module = require('./other_module');
// Functions:
var foo = function () {
};
// Module exports injects:
module.exports.foo = foo;
Run Code Online (Sandbox Code Playgroud)
Fat*_*tie 10
// DO NOT DO THIS
// module.exports = {
// tell, devTell, devTellPermanentLog,
// updateAdmin
// }
// DO THIS:
exports.tell = tell
exports.devTell = devTell
exports.devTellPermanentLog = devTellPermanentLog
exports.updateAdmin = updateAdmin
Run Code Online (Sandbox Code Playgroud)
令人难以置信的是,对于 Node.js 中关于循环的数百字数千字来说,这就是整个解决方案。
这里的所有都是它的。
谢谢你,卡米洛。
如果您没有时间重构,这里有一个值得了解的快速解决方法:
通常你会require在文件的顶部...
var script = require('./script')
function stuff() {
script.farfunction()
}
Run Code Online (Sandbox Code Playgroud)
相反,只需“在函数中”要求它
function stuff() {
var _script = require('./script')
_script.farfunction()
}
Run Code Online (Sandbox Code Playgroud)
小智 8
您可以轻松解决这个问题:在使用 module.exports 的模块中需要任何其他内容之前,只需导出您的数据:
类A.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();
Run Code Online (Sandbox Code Playgroud)
类B.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();
Run Code Online (Sandbox Code Playgroud)
需要最小改变的解决方案是扩展module.exports而不是覆盖它.
a.js - 使用方法的app入口点和模块来自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()`
Run Code Online (Sandbox Code Playgroud)
b.js - 使用方法的模块来自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
}
})
Run Code Online (Sandbox Code Playgroud)
它将工作和生产:
doing b
doing a
Run Code Online (Sandbox Code Playgroud)
虽然此代码不起作用:
a.js
b = require('./b');
module.exports = {
do: function () {
console.log('doing a');
}
};
b.do();
Run Code Online (Sandbox Code Playgroud)
b.js
a = require('./a');
module.exports = {
do: function () {
console.log('doing b');
}
};
a.do();
Run Code Online (Sandbox Code Playgroud)
输出:
node a.js
b.js:7
a.do();
^
TypeError: a.do is not a function
Run Code Online (Sandbox Code Playgroud)
重要的是不要重新分配给module.exports您的对象,因为该对象可能已经被分配给循环中的其他模块!只需在内部分配属性module.exports,其他模块就会看到它们出现。
所以一个简单的解决方案是:
module.exports.firstMember = ___;
module.exports.secondMember = ___;
Run Code Online (Sandbox Code Playgroud)
唯一真正的缺点是需要重复module.exports.多次。
与 lanzz 和 setec 的答案类似,我一直在使用以下模式,感觉更具声明性:
module.exports = Object.assign(module.exports, {
firstMember: ___,
secondMember: ___,
});
Run Code Online (Sandbox Code Playgroud)
将Object.assign()成员复制到exports已提供给其他模块的对象中。
该=分配在逻辑上是多余的,因为它只是设置module.exports为自身,但我使用它是因为它可以帮助我的 IDE (WebStorm) 识别出这firstMember是该模块的属性,因此“转到 -> 声明”(Cmd-B)其他工具将在其他文件中工作。
这种模式不是很漂亮,所以我只在需要解决循环依赖问题时使用它。
它非常适合Reveal 模式,因为您可以轻松地添加和删除对象的导出,特别是在使用 ES6 的属性简写时。
Object.assign(module.exports, {
firstMember,
//secondMember,
});
Run Code Online (Sandbox Code Playgroud)
只需使用exports.someMember = someMember而不是module.exports = { // new object }.
在阅读了 lanzz 的回复后,我终于可以弄清楚这里发生了什么,所以我将在这个问题上给出我的两分钱,扩展他的答案。
让我们看这个例子:
a.js
console.log("a starting");
console.log("a requires b");
const b = require("./b");
console.log("a gets b =", b);
function functionA() {
console.log("function a");
}
console.log("a done");
exports.functionA = functionA;
Run Code Online (Sandbox Code Playgroud)
b.js
console.log("b starting");
console.log("b requires a");
const a = require("./a");
console.log("b gets a =", a);
function functionB() {
console.log("On b, a =", a)
}
console.log("b done");
exports.functionB = functionB;
Run Code Online (Sandbox Code Playgroud)
main.js
const a = require("./a");
const b = require("./b");
b.functionB()
Run Code Online (Sandbox Code Playgroud)
输出
a starting
a requires b
b starting
b requires a
b gets a = {}
b done
a gets b = { functionB: [Function: functionB] }
a done
On b, a = { functionA: [Function: functionA] }
Run Code Online (Sandbox Code Playgroud)
在这里我们可以看到,首先b接收到一个空对象 as a,然后一旦a完全加载,该引用就会通过 更新exports.functionA = functionA。如果您将整个模块替换为另一个对象,通过module.exports,b则将丢失来自 的引用a,因为它将从一开始就指向同一个空对象,而不是指向新对象。
因此,如果您a像这样导出:module.exports = { functionA: functionA },那么输出将是:
a starting
a requires b
b starting
b requires a
b gets a = {}
b done
a gets b = { functionB: [Function: functionB] }
a done
On b, a = {} // same empty object
Run Code Online (Sandbox Code Playgroud)
只有在你需要的时候,懒惰需要什么呢?所以你的b.js看起来如下
var ClassB = function() {
}
ClassB.prototype.doSomethingLater() {
var a = require("./a"); //a.js has finished by now
util.log(a.property);
}
module.exports = ClassB;
Run Code Online (Sandbox Code Playgroud)
当然,最好将所有require语句放在文件的顶部.但有次,我在那里原谅自己挑选出来的东西其它不相关的模块.称之为hack,但有时这比引入进一步的依赖,或添加额外的模块或添加新结构(EventEmitter等)更好
我见过人们做的另一种方法是在第一行导出并将其保存为本地变量,如下所示:
let self = module.exports = {};
const a = require('./a');
// Exporting the necessary functions
self.func = function() { ... }
Run Code Online (Sandbox Code Playgroud)
我倾向于使用这种方法,您知道它有什么缺点吗?
| 归档时间: |
|
| 查看次数: |
66899 次 |
| 最近记录: |