如何在 Angular2 中正确使用依赖注入(DI)?

How to use Dependency Injection (DI) correctly in Angular2?

我一直在努力弄清楚 (DI) 依赖注入在 Angular2 中是如何工作的。每次我尝试将 service/or class 注入我的组件时,我都会 运行 进入很多 problem/issue 。

从不同的 google 文章中,我需要在组件配置中使用 providers: [],或者有时我需要在我的构造函数中使用 @Inject() 或直接在 bootstrap(app, [service]) 中注入?我还看到一些文章要我放置 @injectable 装饰器。

例如:要注入Http,我只需要import{Http}并将Http放在提供者中,但是对于FormBuilder,我需要在构造函数中使用@Inject()

什么时候使用什么有什么经验法则吗?您能否提供一些示例代码片段?谢谢:-)

宽泛的问题,TL;DR 版本


@Injectable()

  • 是一个装饰器,它告诉typescript被装饰的class有dependencies,并不意味着这个class可以注入一些其他.

  • 然后TypeScript知道它需要在构造时通过使用imported依赖项将所需的元数据注入装饰class。

bootstrap(app, [service])

  • bootstrap() 负责在 bootstrapped 时为我们的应用程序创建根注入器。它以提供者列表作为第二个参数,在创建时将直接传递给注入器。

  • 您 bootstrap 您的应用程序包含将在许多地方使用的服务,例如 Http,这也意味着您不需要编写 providers: [Http] 在你的 class 配置中。

providers: [service]

  • providers 还负责将所有服务的参数传递给 Injector .

  • 如果没有 bootstrap()ped,您将服务放在提供商中。并且只在少数地方需要。

@Inject()

  • 也是一个装饰器一个函数,它完成实际注入这些服务的工作
    像这样。 constructor(@Inject(NameService) nameService)
  • 但是如果你使用 TS,你需要做的就是这个 constructor(nameService: NameService) typescript 会处理剩下的。

进一步阅读

希望这对您有所帮助。 :)

I need to either use providers: []

为了依赖注入能够为您创建实例,您需要为这些 类(或其他值)某处.[=30= 注册提供程序]

您在何处注册提供者决定了所创造价值的范围。 Angulars DI 是分层的。
如果您在树的根部注册提供者


>=RC.5

@NgModule({
  providers: [/*providers*/]
  ...
})

或延迟加载模块

static forRoot(config: UserServiceConfig): ModuleWithProviders {
  return {
    ngModule: CoreModule,
    providers: [
      {provide: UserServiceConfig, useValue: config }
    ]
  };
}

<=RC.4

(bootstrap(AppComponent, [Providers})@Component(selector: 'app-component', providers: [Providers])(根组件)


然后请求实例的所有组件和服务都将获得相同的实例。

如果在其中一个子组件中注册了提供程序,则会为该组件的后代提供一个新的(不同的)实例。

如果一个组件请求一个实例(通过构造函数参数),DI 会查找 "upwards" 组件树(从叶开始到根)并获取它找到的第一个提供者。如果之前已创建此提供程序的实例,则使用此实例,否则创建一个新实例。

@Inject()

当组件或服务向 DI 请求值时,例如

constructor(someField:SomeType) {}

DI 按类型 SomeType 查找提供商。如果添加@Inject(SomeType)

constructor(@Inject(SomeType) someField:SomeType) {}

DI 通过传递给 @Inject() 的参数查找提供程序。在上面的示例中,传递给 @Inject() 的参数与参数的类型相同,因此 @Inject(SomeType) 是多余的。

但是,在某些情况下,您需要自定义行为,例如注入配置设置。

constructor(@Inject('someName') someField:string) {}

当您注册了多个时,类型 string 不足以区分特定的配置设置。
配置值需要在某处注册为提供者,例如


>=RC.5

@NgModule({
  providers: [{provide: 'someName', useValue: 'abcdefg'})]
  ...
})
export class AppModule {}

<=RC.4

bootstrap(AppComponent, [provide('someName', {useValue: 'abcdefg'})])

因此,如果构造函数看起来像

,则 FormBuilder 不需要 @Inject()
constructor(formBuilder: FormBuilder) {}

Angular2 中的依赖注入依赖于链接到组件树的分层注入器。

这意味着您可以在不同级别配置提供程序:

  • 在引导时针对整个应用程序。在这种情况下,所有子注入器(组件注入器)都会看到这个提供者并共享与之关联的实例。交互的时候会是同一个实例
  • 针对特定组件及其子组件。与以前相同,但针对特定组件。其他组件不会看到此提供程序。如果您重新定义上面定义的内容(例如在引导时),将改用此提供程序。所以你可以覆盖东西。
  • 为了服务。没有与它们关联的提供者。他们使用来自触发元素的注入器(直接=组件或间接=触发服务链调用的组件)

关于您的其他问题:

  • @可注射。要注入 class,您需要一个装饰器。组件有一个(@Component 一个)但服务很简单 classes。如果一个服务需要注入依赖,你需要这个装饰器。
  • @注入。大多数时候,构造函数参数的类型足以让 Angular2 决定注入什么。在某些情况下(例如,如果您明确使用 OpaqueToken 而不是 class 来注册提供者),您需要指定一些关于注入内容的提示。在这种情况下,您需要使用@Inject.

查看这些问题以获取更多详细信息:

  • What's the best way to inject one service into another in angular 2 (Beta)?

我将添加一些其他答案中未提及的内容。 (在我写这篇文章的时候,这意味着 Thierry、Günter 和 A_Singh 的回答)。

  • 始终将 Injectable() 添加到您创建的服务中。尽管仅当您的服务本身需要注入某些内容时才需要它,但最好始终包含它。
  • directives/components 上的 providers 数组和 NgModules 中的 providers 数组是仅有的两种注册非内置提供者的方法。 (我们不必注册的内置对象的例子有ElementRefApplicationRef等,我们可以简单地注入这些。)
  • 当组件具有 providers 数组时,该组件将获得 Angular 注入器。当某些东西想要注入依赖项时(如构造函数中指定的那样),将咨询注入器。我喜欢将注入器树视为比组件树更简洁的树。第一个可以满足依赖请求的注入器会这样做。这种注入器的层次结构允许依赖关系是单例还是非单例。

为什么使用@Injectable()?

@Injectable() 将 class 标记为可用于注入器实例化。一般来说,注入器在尝试实例化一个没有标记为@Injectable().

的class时会报错

碰巧的是,我们可以从我们的第一个版本的 HeroService 中省略 @Injectable() ,因为它没有注入参数。但是我们现在必须拥有它,因为我们的服务有一个注入的依赖项。我们需要它,因为 Angular 需要构造函数参数元数据才能注入 Logger。

建议:将@INJECTABLE() 添加到每个服务中CLASS 我们建议将 @Injectable() 添加到每个服务 class,即使是那些没有依赖项的服务,因此在技术上不需要它。原因如下:

面向未来:以后添加依赖项时无需记住@Injectable()。

一致性:所有服务都遵循相同的规则,我们不必怀疑为什么缺少装饰器。

注入器还负责实例化像 HeroesComponent 这样的组件。为什么我们没有将 HeroesComponent 标记为 @Injectable()?

如果我们真的想的话,我们可以添加它。没必要,因为HeroesComponent已经标上了@Component,而这个装饰器class(类似于@Directive和@Pipe,我们稍后会了解)是InjectableMetadata的子类型。实际上是 InjectableMetadata 装饰器将 class 识别为注入器实例化的目标。

来源:https://angular.io/docs/ts/latest/guide/dependency-injection.html