我目前正在用nodejs创建一些实验性项目。我用Spring编写过很多Java EE web应用程序,非常欣赏依赖注入的便利性。

现在我很好奇:如何对节点进行依赖注入?或者:我真的需要它吗?因为编程风格不同,是否存在替代概念?

到目前为止,我谈论的是一些简单的事情,比如共享数据库连接对象,但我还没有找到一个让我满意的解决方案。


当前回答

我最近检查了这个线程,原因和OP差不多——我遇到的大多数库都临时重写了require语句。我用这种方法取得了不同程度的成功,所以我最终使用了下面的方法。

在一个快速应用程序的上下文中-我将app.js包装在bootstrap.js文件中:

var path = require('path');
var myapp = require('./app.js');

var loader = require('./server/services/loader.js');

// give the loader the root directory
// and an object mapping module names 
// to paths relative to that root
loader.init(path.normalize(__dirname), require('./server/config/loader.js')); 

myapp.start();

传递给加载器的对象映射是这样的:

// live loader config
module.exports = {
    'dataBaseService': '/lib/dataBaseService.js'
}

// test loader config
module.exports = {
    'dataBaseService': '/mocks/dataBaseService.js'
    'otherService' : {other: 'service'} // takes objects too...
};

然后,不要直接调用require…

var myDatabaseService = loader.load('dataBaseService');

如果加载器中没有别名,那么它将默认为常规require。 这样做有两个好处:我可以交换类的任何版本,并且消除了这种需要 在整个应用程序中使用相对路径名(因此如果我需要下面的自定义库 或以上的当前文件,我不需要遍历,并要求将缓存模块针对相同的关键)。它还允许我在应用程序的任何位置指定模拟,而不是在立即的测试套件中。

为了方便起见,我刚刚发布了一个小的npm模块:

https://npmjs.org/package/nodejs-simple-loader

其他回答

我一直很喜欢IoC的简单理念——“你不需要了解任何环境,需要的时候有人会叫你。”

但是我看到的所有IoC实现都完全相反——它们用更多的东西使代码变得混乱。所以,我创建了我自己的IoC,就像我想要的那样-它保持隐藏和不可见的90%的时间。

它用于MonoJS web框架http://monojs.org

我说的是简单的事情,比如共享一个数据库连接对象 但我还没有找到一个让我满意的解决办法。

它是这样做的——在配置中注册组件一次。

app.register 'db', -> 
  require('mongodb').connect config.dbPath

可以在任何地方使用

app.db.findSomething()

你可以在https://github.com/sinizinairina/mono/blob/master/mono.coffee这里看到完整的组件定义代码(包括DB Connection和其他组件)

这是你必须告诉IoC该做什么的唯一地方,之后所有这些组件都将自动创建和连接,你不再需要在应用程序中看到IoC特定的代码。

国际奥委会本身https://github.com/alexeypetrushin/miconjs

为了很好地测试应用程序,大多数时候最好使用反转控制工具在运行/测试时注入所需的对象。 所以最好不要在模块中直接使用require或import。而是调用DI容器来获取所需的对象。

如果你不想使用第三方库,你可以通过创建一个定制的DI容器来模仿IoC工具的行为。 在测试的时候,你可以模拟你的DI容器,并注入你的假模块用于测试。 下面是一个自定义DI容器的例子,这个版本的容器不支持接口。

myDependecy.js

const myDependecy = {};
export default myDependecy;

myDependecy.myTestFunction = () => {
  console.log("this is as test function.");
};

diContainer.js

import myDependecy from "./myDependecy.js";

const diContainer = {};
export default diContainer;

diContainer.myDependecy = myDependecy;

myModule.js

import diContainer from "./diContainer.js";

function myFunction() {
  diContainer.myDependecy.myTestFunction();
}

我最近检查了这个线程,原因和OP差不多——我遇到的大多数库都临时重写了require语句。我用这种方法取得了不同程度的成功,所以我最终使用了下面的方法。

在一个快速应用程序的上下文中-我将app.js包装在bootstrap.js文件中:

var path = require('path');
var myapp = require('./app.js');

var loader = require('./server/services/loader.js');

// give the loader the root directory
// and an object mapping module names 
// to paths relative to that root
loader.init(path.normalize(__dirname), require('./server/config/loader.js')); 

myapp.start();

传递给加载器的对象映射是这样的:

// live loader config
module.exports = {
    'dataBaseService': '/lib/dataBaseService.js'
}

// test loader config
module.exports = {
    'dataBaseService': '/mocks/dataBaseService.js'
    'otherService' : {other: 'service'} // takes objects too...
};

然后,不要直接调用require…

var myDatabaseService = loader.load('dataBaseService');

如果加载器中没有别名,那么它将默认为常规require。 这样做有两个好处:我可以交换类的任何版本,并且消除了这种需要 在整个应用程序中使用相对路径名(因此如果我需要下面的自定义库 或以上的当前文件,我不需要遍历,并要求将缓存模块针对相同的关键)。它还允许我在应用程序的任何位置指定模拟,而不是在立即的测试套件中。

为了方便起见,我刚刚发布了一个小的npm模块:

https://npmjs.org/package/nodejs-simple-loader

I worked with .Net, PHP and Java for long time so I wanted to have a convenient Dependency Injection in NodeJS too. People said the built-in DI in NodeJS is enough as we can get it with Module. But it didn't satisfy me well. I wanted to keep a Module no more than a Class. Additionally, I wanted the DI to have a full support for Module life cycle management (singleton module, transient module etc.) but with Node module, I had to write manual code very often. Lastly, I wanted to make Unit Test easier. That's why I created a Dependency Injection for myself.

如果您正在寻找DI,请尝试一下。可以在这里找到:https://github.com/robo-creative/nodejs-robo-container。它有完整的文档。并对依赖注入中常见的问题进行了分析,并提出了面向对象的解决方法。希望能有所帮助。

我还写了一个模块来完成这一点,它被称为rewire。只需使用npm安装rewire,然后:

var rewire = require("rewire"),
    myModule = rewire("./path/to/myModule.js"); // exactly like require()

// Your module will now export a special setter and getter for private variables.
myModule.__set__("myPrivateVar", 123);
myModule.__get__("myPrivateVar"); // = 123


// This allows you to mock almost everything within the module e.g. the fs-module.
// Just pass the variable name as first parameter and your mock as second.
myModule.__set__("fs", {
    readFile: function (path, encoding, cb) {
        cb(null, "Success!");
    }
});
myModule.readSomethingFromFileSystem(function (err, data) {
    console.log(data); // = Success!
});

我受到Nathan MacInnes注射剂的启发,但使用了不同的方法。我不使用vm来评估测试模块,事实上我使用节点自己的要求。这样,您的模块的行为与使用require()完全相同(除了您的修改)。此外,完全支持调试。