现在有了 TypeScript,我们在 JavaScript 中有了静态分析和许多 OOP 特性。所以现在是时候在客户端逻辑中进行更好的单元测试了,而且我们需要 IOC 容器来进行依赖注入,以使代码更具可测试性......
那么,是否有人已经体验过这个主题,或者可能知道可以移植到 TypeScript 的 typescript 或 JavaScript 框架的库?
现在有了 TypeScript,我们在 JavaScript 中有了静态分析和许多 OOP 特性。所以现在是时候在客户端逻辑中进行更好的单元测试了,而且我们需要 IOC 容器来进行依赖注入,以使代码更具可测试性......
那么,是否有人已经体验过这个主题,或者可能知道可以移植到 TypeScript 的 typescript 或 JavaScript 框架的库?
我开发了一个名为 InversifyJS 的 IoC 容器,它具有诸如上下文绑定之类的高级依赖注入功能。
您需要遵循3 个基本步骤才能使用它:
注释 API 基于 Angular 2.0:
import { injectable, inject } from "inversify";
@injectable()
class Katana implements IKatana {
public hit() {
return "cut!";
}
}
@injectable()
class Shuriken implements IShuriken {
public throw() {
return "hit!";
}
}
@injectable()
class Ninja implements INinja {
private _katana: IKatana;
private _shuriken: IShuriken;
public constructor(
@inject("IKatana") katana: IKatana,
@inject("IShuriken") shuriken: IShuriken
) {
this._katana = katana;
this._shuriken = shuriken;
}
public fight() { return this._katana.hit(); };
public sneak() { return this._shuriken.throw(); };
}
绑定 API 基于 Ninject:
import { Kernel } from "inversify";
import { Ninja } from "./entities/ninja";
import { Katana } from "./entities/katana";
import { Shuriken} from "./entities/shuriken";
var kernel = new Kernel();
kernel.bind<INinja>("INinja").to(Ninja);
kernel.bind<IKatana>("IKatana").to(Katana);
kernel.bind<IShuriken>("IShuriken").to(Shuriken);
export default kernel;
解析 API 基于 Ninject:
import kernel = from "./inversify.config";
var ninja = kernel.get<INinja>("INinja");
expect(ninja.fight()).eql("cut!"); // true
expect(ninja.sneak()).eql("hit!"); // true
最新版本 (2.0.0) 支持许多用例:
我为打字稿创建了 DI 库 - huject
https://github.com/asvetliakov/huject
例子:
import {Container, FactoryMethod, ConstructorInject, Inject} from 'huject';
class FirstService {
public constructor(param: number) {}
}
class SecondService {
}
@ConstructorInject
class MyController {
@Inject
public service: FirstService;
public second: SecondService;
public constructor(service: SecondService) {
this.second = service;
}
...
}
container.setAllowUnregisteredResolving(true);
container.register(FirstService, [10]); // Register constructor arguments
// FirstService and SecondService will be resolved for container instance
let controller = container.resolve(MyController);
虽然 TypeScript 接口存在问题,但我有 2 个解决方法(使用抽象类或简单类作为接口)
或者,您可以不使用框架并将类用作容器,并将对象工厂用作属性。然后,您可以在测试中继承此类并更改工厂。这种方法是类型安全的,不需要任何装饰器,只需注册类。
class B {
echo() {
alert('test');
}
}
class A {
constructor(private b: B) {
b.echo();
}
}
class Container {
A = () => new A(this.B());
B = singleton(() => new B());
}
var c = new Container();
var a = c.A();
// singleton helper:
function memoize<T>(factory: () => T): () => T {
var memo: T = null;
return function () {
if(!memo) {
memo = factory();
}
return memo;
};
}
var singleton = memoize;
现在,您可以在没有 IOC 部分的 JavaScript 中使用依赖注入。是否编写“手动”解析器、工厂或任何您喜欢的 DI 模式取决于您。
当采用 ECMAScript 6 标准时,它可能使 IOC 的概念在 JavaScript 中成为可能。
我们一直在使用一个简单的依赖注入容器,它使用 AMD 定义/要求 - 类似语法。最初的实现是在 TypeScript 中,尽管下面的博客文章是用普通的旧 JavaScript 呈现的。
http://blog.coolmuse.com/2012/11/11/a-simple-javascript-dependency-injection-container/
定义依赖关系非常简单,不需要大量配置,并且支持类似于 requirejs 的循环依赖解析。
这是一个简单的例子:
// create the kernel
var kernel = new ServiceKernel();
// define service1
kernel.define("service1", function() {
// service1 has a function named foo
return {
foo: function () { return "foo"; }
}
});
// define service2, which depends on service1
kernel.define("service2", ["service1"], function(service1) {
// service2 has a function named foobar
return {
foobar : function() { return service1.foo() + "bar"; }
}
});
// get service2 instance
var service2 = kernel.require("service2");
service2.foobar(); // returns "foobar"
// get both service1 and service2 instances
kernel.require(["service1", "service2"], function(service1, service2) {
alert(service1.foo() + service2.foobar()); // displays foofoobar
});
我曾经将TSyringe用于中小型项目。它工作得很好,但我的结论是我不需要在 JavaScript / TypeScript 中进行依赖注入。
由于 JavaScript 是一种动态语言,所有实现都可以在运行时更改。如果您只想注入存根对象进行单元测试,请使用SinonJS或类似工具。
为什么:
结帐https://github.com/typestack/typedi
这样的事情是可能的:
import "reflect-metadata";
import {Service, Container} from "typedi";
@Service()
class SomeClass {
someMethod() {
}
}
let someClass = Container.get(SomeClass);
someClass.someMethod();