10

现在有了 TypeScript,我们在 JavaScript 中有了静态分析和许多 OOP 特性。所以现在是时候在客户端逻辑中进行更好的单元测试了,而且我们需要 IOC 容器来进行依赖注入,以使代码更具可测试性......

那么,是否有人已经体验过这个主题,或者可能知道可以移植到 TypeScript 的 typescript 或 JavaScript 框架的库?

4

7 回答 7

20

我开发了一个名为 InversifyJS 的 IoC 容器,它具有诸如上下文绑定之类的高级依赖注入功能。

您需要遵循3 个基本步骤才能使用它:

1.添加注释

注释 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(); };

}

2. 声明绑定

绑定 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;

3.解决依赖关系

解析 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) 支持许多用例:

  • 内核模块
  • 内核中间件
  • 使用类、字符串文字或符号作为依赖标识符
  • 注入常​​量值
  • 注入类构造函数
  • 工厂注入
  • 汽车厂
  • 注入提供者(异步工厂)
  • 激活处理程序(用于注入代理)
  • 多次注射
  • 标记绑定
  • 自定义标签装饰器
  • 命名绑定
  • 上下文绑定
  • 友好的异常(例如循环依赖)

您可以在https://github.com/inversify/InversifyJS了解更多信息

于 2015-05-07T22:33:29.727 回答
5

我为打字稿创建了 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 个解决方法(使用抽象类或简单类作为接口)

于 2015-08-31T00:13:46.740 回答
5

或者,您可以不使用框架并将类用作容器,并将对象工厂用作属性。然后,您可以在测试中继承此类并更改工厂。这种方法是类型安全的,不需要任何装饰器,只需注册类。

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;
于 2015-10-15T08:35:09.030 回答
2

现在,您可以在没有 IOC 部分的 JavaScript 中使用依赖注入。是否编写“手动”解析器、工厂或任何您喜欢的 DI 模式取决于您。

当采用 ECMAScript 6 标准时,它可能使 IOC 的概念在 JavaScript 中成为可能。

于 2012-10-09T10:28:09.543 回答
2

我们一直在使用一个简单的依赖注入容器,它使用 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

});
于 2012-11-12T06:41:21.487 回答
1

我曾经将TSyringe用于中小型项目。它工作得很好,但我的结论是我不需要在 JavaScript / TypeScript 中进行依赖注入。

由于 JavaScript 是一种动态语言,所有实现都可以在运行时更改。如果您只想注入存根对象进行单元测试,请使用SinonJS或类似工具。

为什么:

  • 它给您的代码带来了不必要的复杂性和冗长的样板文件。
  • 一切都应该写成 Class,因此 TypeScript 无法为某些方法推断出正确的类型,这意味着您应该手动为它们键入参数。
  • 大多数依赖注入框架依赖于 TypeScript 装饰器,编译速度很慢。并且该功能仍处于试验阶段(这可能会随着时间的推移而改变)
于 2021-06-22T17:50:54.457 回答
0

结帐https://github.com/typestack/typedi

这样的事情是可能的:

import "reflect-metadata";
import {Service, Container} from "typedi";

@Service()
class SomeClass {

    someMethod() {
    }

}

let someClass = Container.get(SomeClass);
someClass.someMethod();
于 2018-12-03T12:15:23.550 回答