让我们从一些参考代码开始
var express = require('express');
var app = express();
var session = require('express-session');
app.use(session({
store: require('connect-session-knex')()
}));
我在这里有几个问题,如果你知道答案,我想回答:
每次在Nodejs中调用require
时,该命名依赖注入是什么?或依赖注入的真正含义是什么?
我之所以这样问,是因为我一直在阅读关于Node的内容,而且我看到有人在谈论module
或module.export
模式,我很困惑, module
与dependency
相同?
所以,我需要的是关于依赖注入的明确解释,以及何时/何时需要注入依赖项。 。
答案 0 :(得分:8)
依赖注入与正常的模块设计有些相反。在正常的模块设计中,模块使用require()
加载所需的所有其他模块,目的是使调用者能够轻松使用您的模块。调用者可以只在模块中require()
,模块将加载所需的所有其他内容。
使用依赖注入,而不是模块加载它需要的东西,调用者需要传入模块所需的东西(通常是对象)。这可以使某些类型的测试更容易,并且可以更容易地模拟某些事情以便于测试。
每次在Nodejs中调用require时,都是指定的依赖项 注射?或依赖注入的真正含义是什么?
没有。当一个模块执行require()
加载它自己的依赖注入而非依赖注入时。
我之所以这样问,是因为我一直在阅读 节点,我看到有人在谈论模块或 module.export模式,我很困惑,模块是一样的 依赖
模块与依赖项不同。正常的模块设计允许您require()
只是模块,并返回一系列可以使用的导出。模块本身处理其自身依赖项的加载(通常使用模块内部的require()
。)
以下是一些讨论使用依赖注入的一些优点/缺点的文章。最好的我可以说主要的优点是通过允许更容易模拟依赖对象(如数据库)来简化单元测试。
When to use Dependency Injection
When is it not appropriate to use dependency injection
Why should we use dependency injection
使用依赖注入的经典案例是模块依赖于数据库接口。如果模块加载它自己的数据库,那么该模块基本上是硬连接到该特定数据库。模块内置的体系结构不允许调用者指定应使用的存储类型。
但是,如果设置了模块,那么当调用者加载并初始化模块时,它必须传入一个实现特定数据库API的对象,然后调用者可以自由决定应该使用哪种类型的数据库。可以使用符合API合同的任何数据库。但是,调用者负担选择和加载特定数据库的负担。也可能存在混合情况,其中模块具有默认使用的内置数据库,但调用者可以提供他们自己的对象,如果在模块构造函数或模块初始化中提供它将使用它。
答案 1 :(得分:2)
想象一下这段代码。
var someModule = require('pathToSomeModule');
someModule();
在这里,我们不依赖于名称,而是依赖于该文件的路径。我们每次都使用SAME文件。
让我们看看角度的方式(对于客户,我知道,请耐心等待)
app.controller('someCtrl', function($scope) {
$scope.foo = 'bar';
});
我知道客户端js没有文件导入/导出,但它是你应该看的基本概念。这个控制器没有指定$ scope变量ACTUALLY是什么,它只知道angular给它一些CALLED $ scope。
这是控制反转 就像说,别打电话给我,我会打电话给你
现在让我们用类似服务容器的东西来实现我们的原始代码(对此有许多不同的解决方案,容器不是唯一的选择)
// The only require statement
var container = require('pathToContainer')
var someModule = container.resolve('someModule');
someModule();
我们在这里取得了什么成就?现在,我们只需知道一件事,即容器(或您选择的任何抽象)。我们不知道someModule
实际上是什么,或者它的源文件在哪里,只是它是我们从容器中得到的。这样做的好处是,如果我们想要使用someModule
的不同实现,只要它符合与orignal相同的API,我们就可以在ENTIRE应用中将其替换为一个位置。容器。现在,每个调用someModule
的模块都将获得新的实现。这个想法是,当你创建一个模块时,你定义了用来与它交互的api。如果不同的实现都符合单个api(或者你编写符合它的适配器),那么你可以换掉像脏内衣这样的实现,你的应用就可以了。
这种做法并不适合所有人,有些人讨厌在集装箱周围徘徊。
我个人认为,我宁愿编写一个接口(实现之间的一致api)而不是具体实现的代码。
node.js中的Dependency Injection 的实际示例
// In a file, far, far, away
module.exports = function(dependencyA, dependencyB) {
dependencyA();
dependencyB();
}
// In another file, the `caller`
// This is where the actual, concrete implementation is stored
var depA = someConcreteImplementation;
var depB = someOtherConcreteImplementation;
var someModule = require('pathToSomeModule');
someModule(depA, depB);
这样做的缺点是,现在调用者需要知道你的依赖是什么。有些人对此感到欣慰,喜欢它,其他人则认为这很麻烦。
我个人更喜欢这种方法。
如果您没有使用babel,或者在幕后更改功能的内容,您可以使用此方法获取角度样式参数解析
http://krasimirtsonev.com/blog/article/Dependency-injection-in-JavaScript
然后你可以解析从require
得到的函数,而不是使用容器。
答案 2 :(得分:0)
npm i -P injection-js
它基于angular2 injector,因此注入非常简单。
以下是自述文件中的打字稿版本。
import 'reflect-metadata';
import { ReflectiveInjector, Injectable, Injector } from 'injection-js';
class Http {}
@Injectable()
class Service {
constructor(private http: Http) {}
}
@Injectable()
class Service2 {
constructor(private injector: Injector) {}
getService(): void {
console.log(this.injector.get(Service) instanceof Service);
}
createChildInjector(): void {
const childInjector = ReflectiveInjector.resolveAndCreate([
Service
], this.injector);
}
}
const injector = ReflectiveInjector.resolveAndCreate([
Service,
Http
]);
console.log(injector.get(Service) instanceof Service);