Answers:
简而言之,您不需要像C#/ Java中那样的依赖项注入容器或服务定位器。由于Node.js利用module pattern
,因此无需执行构造函数或属性注入。虽然您仍然可以。
JS的伟大之处在于,您可以修改几乎所有内容以实现所需的功能。在进行测试时,这很方便。
看我非常very脚的人为例子。
MyClass.js
:
var fs = require('fs');
MyClass.prototype.errorFileExists = function(dir) {
var dirsOrFiles = fs.readdirSync(dir);
for (var d in dirsOrFiles) {
if (d === 'error.txt') return true;
}
return false;
};
MyClass.test.js
:
describe('MyClass', function(){
it('should return an error if error.txt is found in the directory', function(done){
var mc = new MyClass();
assert(mc.errorFileExists('/tmp/mydir')); //true
});
});
注意如何MyClass
依赖fs
模块?正如@ShatyemShekhar所提到的,您确实可以像使用其他语言一样进行构造函数或属性注入。但这在Javascript中不是必需的。
在这种情况下,您可以做两件事。
您可以对fs.readdirSync
方法进行存根,也可以在调用时返回完全不同的模块require
。
方法1:
var oldmethod = fs.readdirSync;
fs.readdirSync = function(dir) {
return ['somefile.txt', 'error.txt', 'anotherfile.txt'];
};
*** PERFORM TEST ***
*** RESTORE METHOD AFTER TEST ****
fs.readddirSync = oldmethod;
方法2:
var oldrequire = require
require = function(module) {
if (module === 'fs') {
return {
readdirSync: function(dir) {
return ['somefile.txt', 'error.txt', 'anotherfile.txt'];
};
};
} else
return oldrequire(module);
}
关键是利用Node.js和Javascript的功能。请注意,我是CoffeeScript专家,所以我的JS语法在某处可能不正确。另外,我并不是说这是最好的方法,而是一种方法。Javascript专家可能会与其他解决方案配合使用。
更新:
这应该解决您有关数据库连接的特定问题。我将为您创建一个单独的模块,以封装您的数据库连接逻辑。像这样:
MyDbConnection.js
:(请务必选择一个更好的名称)
var db = require('whichever_db_vendor_i_use');
module.exports.fetchConnection() = function() {
//logic to test connection
//do I want to connection pool?
//do I need only one connection throughout the lifecyle of my application?
return db.createConnection(port, host, databasename); //<--- values typically from a config file
}
然后,任何需要数据库连接的MyDbConnection
模块都将仅包含您的模块。
SuperCoolWebApp.js
:
var dbCon = require('./lib/mydbconnection'); //wherever the file is stored
//now do something with the connection
var connection = dbCon.fetchConnection(); //mydbconnection.js is responsible for pooling, reusing, whatever your app use case is
//come TEST time of SuperCoolWebApp, you can set the require or return whatever you want, or, like I said, use an actual connection to a TEST database.
不要逐字遵循此示例。在尝试传达您利用module
模式来管理依赖项时,这是一个la脚的例子。希望这会有所帮助。
require('my_logger_library')
,则使用我的组件的人将不得不覆盖使用他们自己的库的要求。相反,我可以允许人们传递将记录器实现包装到组件“构造函数”或“初始化”方法中的回调。那就是DI的目的。
require
是在Node.js中管理依赖项的方法,它肯定是直观且有效的,但也有其局限性。
我的建议是看一下当今可用于Node.js的一些依赖注入容器,以了解其优缺点。他们之中有一些是:
仅举几个。
现在真正的问题是,与简单的容器相比,使用Node.js DI容器可以实现什么require
?
优点:
缺点:
require
绝对不使用感觉就像您偏离了Node的思维方式。与任何与软件开发相关的内容一样,在DI或其他require
条件之间进行选择取决于您的要求,系统复杂性和编程风格。
我知道这个线程在这一点上已经相当老了,但是我认为我对此很感兴趣。TL; DR的原因在于,由于JavaScript具有无类型的动态特性,因此您实际上可以做很多事情而无需诉诸于依赖注入(DI)模式或使用DI框架。但是,随着应用程序变得越来越大,越来越复杂,DI无疑可以帮助您维护代码的可维护性。
要了解为什么在JavaScript中DI并没有那么重要,研究一下C#之类的强类型语言会很有帮助。(对不懂C#的人表示歉意,但是应该很容易理解。)假设我们有一个描述汽车及其喇叭的应用程序。您将定义两个类:
class Horn
{
public void Honk()
{
Console.WriteLine("beep!");
}
}
class Car
{
private Horn horn;
public Car()
{
this.horn = new Horn();
}
public void HonkHorn()
{
this.horn.Honk();
}
}
class Program
{
static void Main()
{
var car = new Car();
car.HonkHorn();
}
}
这样编写代码几乎没有问题。
Car
类被紧密耦合到特定的实施方式中的喇叭的Horn
类。如果要更改汽车使用的喇叭的类型,则Car
即使该喇叭的用法不变,我们也必须修改该类。这也使测试变得困难,因为我们无法将Car
类与其依赖项(即Horn
类)隔离开来进行测试。Car
班负责的生命周期Horn
类。在像这样的简单示例中,这不是什么大问题,但是在实际应用程序中,依赖项将具有依赖项,而依赖项将具有依赖项等。Car
类将需要负责创建其依赖项的整个树。这不仅复杂而且重复,而且违反了班级的“单一责任”。它应该专注于成为一辆汽车,而不是创建实例。现在,让我们重构它以使用依赖项注入模式。
interface IHorn
{
void Honk();
}
class Horn : IHorn
{
public void Honk()
{
Console.WriteLine("beep!");
}
}
class Car
{
private IHorn horn;
public Car(IHorn horn)
{
this.horn = horn;
}
public void HonkHorn()
{
this.horn.Honk();
}
}
class Program
{
static void Main()
{
var horn = new Horn();
var car = new Car(horn);
car.HonkHorn();
}
}
我们在这里做了两件事。首先,我们介绍了我们的Horn
类实现的接口。这使我们可以将Car
类编码为接口,而不是特定的实现。现在,代码可以采用任何实现的方法IHorn
。其次,我们已经删除了号角实例Car
并将其传递给它。这样可以解决上述问题,并将其留给应用程序的主要功能来管理特定实例及其生命周期。
这意味着可以为汽车引入一种新型的喇叭,而无需碰到其他人Car
:
class FrenchHorn : IHorn
{
public void Honk()
{
Console.WriteLine("le beep!");
}
}
main可以只注入FrenchHorn
类的实例。这也大大简化了测试。您可以创建一个MockHorn
类以注入到Car
构造函数中,以确保仅Car
在隔离状态下测试该类。
上面的示例显示了手动依赖项注入。通常,DI是通过框架(例如,C#世界中的Unity或Ninject)完成的。这些框架将通过遍历依赖图并根据需要创建实例来为您完成所有依赖关系接线。
现在让我们来看一下Node.js中的相同示例。我们可能会将代码分为3个模块:
// horn.js
module.exports = {
honk: function () {
console.log("beep!");
}
};
// car.js
var horn = require("./horn");
module.exports = {
honkHorn: function () {
horn.honk();
}
};
// index.js
var car = require("./car");
car.honkHorn();
因为JavaScript是无类型的,所以我们没有像以前那样的紧密耦合。不需要接口(也不存在),因为car
模块只会尝试honk
在horn
模块导出的任何内容上调用该方法。
另外,由于Node require
缓存了所有内容,因此模块本质上是存储在容器中的单例。require
在该horn
模块上执行的任何其他模块将获得完全相同的实例。这使得共享单例对象(如数据库连接)变得非常容易。
现在仍然存在car
模块负责获取其自身依赖关系的问题horn
。如果您想让汽车的喇叭使用其他模块,则必须更改模块中的require
语句car
。这不是很常见的事情,但是确实会导致测试问题。
人们处理测试问题的通常方法是使用proxyquire。由于JavaScript具有动态特性,proxyquire会拦截对require的调用并返回您提供的所有存根/模拟。
var proxyquire = require('proxyquire');
var hornStub = {
honk: function () {
console.log("test beep!");
}
};
var car = proxyquire('./car', { './horn': hornStub });
// Now make test assertions on car...
对于大多数应用程序而言,这已绰绰有余。如果它适用于您的应用程序,则选择它。但是,以我的经验来看,随着应用程序变得越来越大,越来越复杂,维护这样的代码变得越来越困难。
Node.js非常灵活。如果您对上述方法不满意,则可以使用依赖项注入模式编写模块。在这种模式下,每个模块都导出一个工厂函数(或一个类构造函数)。
// horn.js
module.exports = function () {
return {
honk: function () {
console.log("beep!");
}
};
};
// car.js
module.exports = function (horn) {
return {
honkHorn: function () {
horn.honk();
}
};
};
// index.js
var horn = require("./horn")();
var car = require("./car")(horn);
car.honkHorn();
这与前面的C#方法非常相似,因为index.js
模块负责实例生命周期和接线。单元测试非常简单,因为您只需将模拟/存根传递给函数即可。同样,如果这对您的应用程序足够好,请继续使用它。
与C#不同,没有建立的标准DI框架可帮助您进行依赖管理。npm注册表中有许多框架,但没有一个被广泛采用。在其他答案中已经引用了许多这些选项。
我对任何可用的选项都不满意,所以我写了自己的bolus。Bolus旨在与上面以DI风格编写的代码一起使用,并试图使其非常干燥且非常简单。使用完全相同的car.js
和horn.js
模块上方,你可以重写index.js
与丸的模块:
// index.js
var Injector = require("bolus");
var injector = new Injector();
injector.registerPath("**/*.js");
var car = injector.resolve("car");
car.honkHorn();
基本思想是创建一个注射器。您可以在进样器中注册所有模块。然后,您只需解决所需的内容。Bolus将遍历依赖关系图,并根据需要创建和注入依赖关系。在像这样的玩具示例中,您不会节省太多,但是在具有复杂依赖树的大型应用程序中,节省的费用是巨大的。
Bolus支持许多漂亮的功能,例如可选的依赖项和测试全局变量,但是相对于标准的Node.js方法,我看到了两个主要优点。首先,如果您有很多类似的应用程序,则可以为基础创建一个私有npm模块,该模块创建一个注入器并在其上注册有用的对象。然后,您的特定应用可以根据需要添加,覆盖和解析,就像AngularJS的方式一样喷油器工作。其次,您可以使用推注来管理各种依赖性上下文。例如,您可以使用中间件为每个请求创建子注入器,并在注入器上注册用户ID,会话ID,记录器等,以及依赖于这些模块的任何模块。然后解决您需要处理请求的问题。这为您提供了每个请求的模块实例,并避免了将记录器等传递给每个模块函数调用。
proxyquire
诸如这样的替代方法,sinon
它允许您进行非常简洁的模拟,例如let readFileStub = sinon.stub(fs, 'readFile').yields(new Error('something went wrong'));
,随后对的调用fs.readFile
将返回错误,直到您通过还原存根为止readFileStub.restore()
。我个人认为DI的使用值得怀疑,因为我觉得它几乎需要使用类/对象,考虑到javascript的功能倾向,这是一个可疑的假设。
jest
,rewire
,proxyquire
,等?谢谢。
我还编写了一个模块来完成此任务,称为rewire。只需使用npm install 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()
(除了您的修改)。完全支持调试。
我为此目的而建造了Electrolyte。其他的依赖注入解决方案对我的口味来说太具有侵入性,而与全局混乱require
是我的特别不满。
Electrolyte包含模块,特别是那些导出“设置”功能的模块,就像您在Connect / Express中间件中看到的那样。本质上,这些类型的模块只是它们返回的某些对象的工厂。
例如,创建数据库连接的模块:
var mysql = require('mysql');
exports = module.exports = function(settings) {
var connection = mysql.createConnection({
host: settings.dbHost,
port: settings.dbPort
});
connection.connect(function(err) {
if (err) { throw err; }
});
return connection;
}
exports['@singleton'] = true;
exports['@require'] = [ 'settings' ];
您在底部看到的是注释,这是Electrolyte用来实例化和注入依赖项的额外元数据,可自动将应用程序的组件连接在一起。
要创建数据库连接:
var db = electrolyte.create('database');
电解质可传递地遍历@require
'd依赖关系,并将实例作为自变量注入到导出函数中。
关键是这是微创的。该模块完全可用,与电解质本身无关。这意味着您的单元测试可以只测试被测模块,传递模拟对象,而无需其他依赖来重新连接内部。
当运行整个应用程序时,Electrolyte在模块间级别介入,无需将全局变量,单例对象或过多的管道连接在一起即可将它们连接在一起。
connect()
引发时在已发布的代码中会发生什么?即使我对Node的MySql API并不熟悉,我也希望此调用是异步的,因此插图并不十分清楚。
ioc.create
从单元测试中调用。单元测试应仅测试被测模块,而不引入其他依赖项,包括电解质。遵循此建议,您可以做objToTest = require('modulename')(mockObj1, mockObj2);
我自己调查了一下。我不喜欢引入魔术依赖性utils库,该库提供劫持模块导入的机制。取而代之的是,我为团队提出了“设计指南”,以明确声明可以通过在模块中引入工厂函数导出来模拟哪些依赖关系。
我广泛使用ES6功能进行参数设置和结构分解,以避免出现一些样板,并提供了一种命名依赖覆盖机制。
这是一个例子:
import foo from './utils/foo';
import bob from './utils/bob';
// We export a factory which accepts our dependencies.
export const factory = (dependencies = {}) => {
const {
// The 'bob' dependency. We default to the standard 'bob' imp if not provided.
$bob = bob,
// Instead of exposing the whole 'foo' api, we only provide a mechanism
// with which to override the specific part of foo we care about.
$doSomething = foo.doSomething // defaults to standard imp if none provided.
} = dependencies;
return function bar() {
return $bob($doSomething());
}
}
// The default implementation, which would end up using default deps.
export default factory();
这是用法的一个例子
import { factory } from './bar';
const underTest = factory({ $bob: () => 'BOB!' }); // only override bob!
const result = underTest();
对不熟悉ES6的语法感到抱歉。
我最近检查该线程的原因与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');
如果加载程序中没有别名-那么它将默认为常规需求。这有两个好处:我可以交换该类的任何版本,并且它消除了在整个应用程序中使用相对路径名的需要(因此,如果我需要在当前文件之下或之上的自定义lib,则无需遍历,并且require将针对相同的密钥缓存模块)。它还使我可以在应用程序中的任何地方(而不是在即时测试套件中)指定模拟。
为了方便起见,我刚刚发布了一个小npm模块:
现实情况是,您可以在没有IoC容器的情况下测试node.js,因为JavaScript是一种真正的动态编程语言,并且您可以在运行时修改几乎所有内容。
考虑以下:
import UserRepository from "./dal/user_repository";
class UserController {
constructor() {
this._repository = new UserRepository();
}
getUsers() {
this._repository.getAll();
}
}
export default UserController;
因此,您可以在运行时覆盖组件之间的耦合。我想认为我们应该以解耦我们的JavaScript模块为目标。
实现真正去耦的唯一方法是删除对的引用UserRepository
:
class UserController {
constructor(userRepository) {
this._repository = userRepository;
}
getUsers() {
this._repository.getAll();
}
}
export default UserController;
这意味着您将需要在其他地方进行对象组合:
import UserRepository from "./dal/user_repository";
import UserController from "./dal/user_controller";
export default new UserController(new UserRepository());
我喜欢将对象组合委托给IoC容器的想法。您可以在文章JavaScript中依赖项反转的当前状态中了解有关此想法的更多信息。本文试图揭穿一些“ JavaScript IoC容器神话”:
误解1:JavaScript中没有IoC容器的地方
误区二:我们不需要IoC容器,我们已经有了模块加载器!
误解三:依赖倒置===注入依赖
如果您也喜欢使用IoC容器的想法,可以看看InversifyJS。最新版本(2.0.0)支持许多用例:
您可以在InversifyJS上了解更多信息。
对于ES6,我开发了此容器 https://github.com/zazoomauro/node-dependency-injection
import {ContainerBuilder} from 'node-dependency-injection'
let container = new ContainerBuilder()
container.register('mailer', 'Mailer')
然后,您可以在容器中设置例如运输方式的选择:
import {ContainerBuilder} from 'node-dependency-injection'
let container = new ContainerBuilder()
container
.register('mailer', 'Mailer')
.addArgument('sendmail')
由于您已经将传输的选择从实现中分离到了容器中,因此该类现在更加灵活。
现在,邮件服务已在容器中,您可以将其作为其他类的依赖项进行注入。如果您有一个类似NewsletterManager的类:
class NewsletterManager {
construct (mailer, fs) {
this._mailer = mailer
this._fs = fs
}
}
export default NewsletterManager
在定义newsletter_manager服务时,邮件服务尚不存在。使用Reference类告诉容器在初始化新闻通讯管理器时注入邮件服务:
import {ContainerBuilder, Reference, PackageReference} from 'node-dependency-injection'
import Mailer from './Mailer'
import NewsletterManager from './NewsletterManager'
let container = new ContainerBuilder()
container
.register('mailer', Mailer)
.addArgument('sendmail')
container
.register('newsletter_manager', NewsletterManager)
.addArgument(new Reference('mailer'))
.addArgument(new PackageReference('fs-extra'))
您还可以使用配置文件(例如Yaml,Json或JS文件)设置容器
可以出于多种原因来编译服务容器。这些原因包括检查任何潜在问题,例如循环引用,并使容器更有效。
container.compile()
这取决于您的应用程序的设计。显然,您可以执行类似Java的注入,在其中创建类的对象,并在构造函数中传递这样的依赖关系。
function Cache(store) {
this._store = store;
}
var cache = new Cache(mysqlStore);
如果您不使用JavaScript进行OOP,则可以创建一个初始化函数来设置所有内容。
但是,您可以采用另一种方法,这种方法在基于事件的系统(例如node.js)中更为常见。如果您可以将应用程序建模为仅(在大多数情况下)对事件起作用,那么您所需要做的就是设置一切(我通常通过调用init函数来完成)并从存根发出事件。这使得测试相当容易且可读。
我一直喜欢IoC概念的简单性-“您不必了解任何环境,需要时就会有人打电话给您”
但是我看到的所有IoC实现都完全相反-它们使代码杂乱无章。因此,我创建了自己的IoC,该IoC可以按我希望的方式工作-90%的时间保持隐藏和不可见。
在MonoJS Web框架http://monojs.org中使用
到目前为止,我在谈论简单的事情,例如共享数据库连接对象,但是我还没有找到一个令我满意的解决方案。
就是这样-在config中注册一次组件。
app.register 'db', ->
require('mongodb').connect config.dbPath
并在任何地方使用
app.db.findSomething()
您可以在这里https://github.com/sinizinairina/mono/blob/master/mono.coffee看到完整的组件定义代码(带有DB Connection和其他组件)。
在必须告诉IoC该怎么做之后,这是唯一的地方,之后所有这些组件都将自动创建并连接,并且您不再需要在应用程序中看到IoC特定的代码。
我认为我们仍需要在Node.js中进行依赖注入,因为它可以放宽服务之间的依赖关系,并使应用程序更清晰。
受Spring Framework的启发,我还实现了自己的模块以支持Node.js中的依赖注入。我的模块也能够检测code changes
和auto reload
服务,而无需重新启动应用程序。
请访问我的项目:Buncha-IoC容器
谢谢!
了解下垂(Node.js的一个简单但功能强大的依赖项注入和实体(文件)管理框架)
我长期使用.Net,PHP和Java,因此我也想在NodeJS中使用方便的依赖注入。人们说,NodeJS中的内置DI足够了,因为我们可以通过Module获得它。但这并不令我满意。我想保留一个模块不超过一个类。另外,我希望DI对模块生命周期管理(单模块,瞬态模块等)提供全面支持,但是对于Node模块,我不得不经常编写手动代码。最后,我想简化单元测试。这就是为什么我为自己创建了一个依赖注入。
如果您正在寻找DI,请尝试一下。可以在这里找到:https : //github.com/robo-creative/nodejs-robo-container。完全记录在案。它还解决了DI的一些常见问题以及如何以OOP方式解决它们。希望能帮助到你。
我最近创建了一个名为circuitbox的库,该库使您可以将依赖注入与node.js一起使用。与我见过的许多基于依赖查找的库相比,它确实实现了依赖注入。Circuitbox还支持异步创建和初始化例程。下面是一个示例:
假设以下代码位于名为consoleMessagePrinter.js的文件中
'use strict';
// Our console message printer
// deps is injected by circuitbox with the dependencies
function ConsoleMessagePrinter(deps) {
return {
print: function () {
console.log(deps.messageSource.message());
}
};
}
module.exports = ConsoleMessagePrinter;
假设以下内容位于main.js文件中
'use strict';
// our simple message source
// deps is injected by circuitbox with the dependencies
var simpleMessageSource = function (deps) {
return {
message: function () {
return deps.message;
}
};
};
// require circuitbox
var circuitbox = require('../lib');
// create a circuitbox
circuitbox.create({
modules: [
function (registry) {
// the message to be used
registry.for('message').use('This is the message');
// define the message source
registry.for('messageSource').use(simpleMessageSource)
.dependsOn('message');
// define the message printer - does a module.require internally
registry.for('messagePrinter').requires('./consoleMessagePrinter')
.dependsOn('messageSource');
}
]
}).done(function (cbx) {
// get the message printer and print a message
cbx.get('messagePrinter').done(function (printer) {
printer.print();
}, function (err) {
console.log('Could not recieve a printer');
return;
});
}, function (err) {
console.log('Could not create circuitbox');
});
Circuitbox允许您定义组件并将其依赖项声明为模块。初始化后,便可以检索组件。Circuitbox自动注入目标组件所需的所有组件,并提供给您使用。
该项目为Alpha版本。欢迎您提出意见,想法和反馈。
希望能帮助到你!
我认为其他职位在使用DI的论点上做得很好。对我来说,原因是
在不知道其路径的情况下注入依赖项。这意味着,如果您更改磁盘上的模块位置或与另一个模块交换位置,则无需触摸依赖于该模块的每个文件。
它使模拟测试依赖关系变得容易得多,而无需以require
没有问题的方式重写全局函数的痛苦。
它可以帮助您将应用程序组织和推理为松散耦合的模块。
但是我很难找到一个我和我的团队可以轻松采用的DI框架。所以我最近基于这些功能构建了一个名为deppie的框架
require
模块它应该像这样灵活而简单:
var MyClass1 = function () {}
var MyClass2 = function (myService1) {
// myService1.should.be.instanceof(MyClass1);
}
container.register('myService1', MyClass1);
container.register('myService2', MyClass2, ['myService1']);
我在node.js中写了有关依赖注入的文章。
我希望它可以帮助您。
Node.js与其他平台一样需要DI。如果您要构建大型项目,DI将使模拟代码依赖关系和彻底测试代码变得更加容易。
例如,您的数据库层模块不仅仅需要在您的业务代码模块中使用,因为在对这些业务代码模块进行单元测试时,dao将加载并连接到数据库。
一种解决方案是将依赖项作为模块参数传递:
module.exports = function (dep1, dep2) {
// private methods
return {
// public methods
test: function(){...}
}
}
这样,可以轻松自然地模拟依赖关系,并且您可以专注于测试代码,而无需使用任何棘手的第三方库。
还有其他解决方案(百老汇,建筑师等)可以为您提供帮助。尽管他们可能做的比您想要的更多,或使用起来更混乱。
我开发了一个库,该库以一种简单的方式处理依赖项注入,从而减少了样板代码。每个模块均由唯一的名称和控制器功能定义。控制器的参数反映了模块的依赖性。
阅读更多关于KlarkJS的信息
简要示例:
KlarkModule(module, 'myModuleName1', function($nodeModule1, myModuleName2) {
return {
log: function() { console.log('Hello from module myModuleName1') }
};
});
myModuleName1
是模块的名称。$nodeModule1
是的外部库node_module
。名称解析为node-module1
。前缀$
表示它是一个外部模块。myModuleName2
是内部模块的名称。myModuleName1
。在回答我自己的DI模块上的一个问题时,我发现了这个问题,询问为什么有人需要使用DI系统进行NodeJS编程。
答案显然倾向于该线程中给出的答案:这取决于。两种方法都需要权衡,阅读此问题的答案可以很好地解决它们。
因此,此问题的真正答案应该是,在某些情况下,您将使用DI系统,而在其他情况下则不会。
就是说,作为开发人员,您想要的是不要重复自己,并在各种应用程序中重用您的服务。
这意味着我们应该编写可以在DI系统中使用但不依赖于DI库的服务。对我来说,这意味着我们应该编写这样的服务:
module.exports = initDBService;
// Tells any DI lib what it expects to find in it context object
// The $inject prop is the de facto standard for DI imo
initDBService.$inject = ['ENV'];
// Note the context object, imo, a DI tool should bring
// services in a single context object
function initDBService({ ENV }) {
/// actual service code
}
这样,无论是否使用DI工具,您的服务都可以正常工作。
TypeDI是这里提到的最甜的,请在TypeDI中查看此代码
import "reflect-metadata";
import {Service, Container} from "typedi";
@Service()
class SomeClass {
someMethod() {
}
}
let someClass = Container.get(SomeClass);
someClass.someMethod();
看看下面的代码:
import {Container, Service, Inject} from "typedi";
// somewhere in your global app parameters
Container.set("authorization-token", "RVT9rVjSVN");
@Service()
class UserRepository {
@Inject("authorization-token")
authorizationToken: string;
}