繁体   English   中英

当我们实际上可以在不使用装饰器的情况下注入服务时,为什么要使用 @Injectable() 装饰器?

[英]Why do we use @Injectable() decorator , when we can actually inject services without using the decorator as well?

我正在做一个项目,遇到了一个没有 @Injectable() 装饰器的服务,并且工作正常。 到目前为止,我的印象是,在 angular 中,如果我们想实现 DI,我们必须使用 @injectable() 装饰器并使用提供程序进行配置。 使用提供者配置服务是强制性的,但似乎使用 @injectable() 装饰器不是。 它仅在某些服务中使用。 我注意到使用装饰器的服务和不使用装饰器的服务的唯一区别是前者本身有一些依赖关系,而后者没有

我有两种服务:

类型1:

   export class SharedService {
   //do something
   }

类型2:

   @Injectable()
   export class MainCreditService {

     constructor(private http: Http, private config: Config) {
     this.config.EndPoints.subscribe(endpointObj =>  {
        this.environment = endpointObj;
    });
   }

app.module.ts

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { FormsModule, ReactiveFormsModule} from '@angular/forms';
import { HttpClientModule} from '@angular/common/http'

@NgModule({

declarations: [
AppComponent,
],

imports: [
BrowserModule,
HttpClientModule,
FormsModule,
ReactiveFormsModule,
AppRoutingModule,
  ],

exports: [],
providers: [MainService,SharedService],
bootstrap: [AppComponent]
 })
export class AppModule { }

app.component.ts

import { Component } from '@angular/core';
import { HttpClient } from "@angular/common/http";

import { MainService } from '../../services/main.service';
import { SharedService } from '../../services/shared.service';
 
@Component({
 selector: 'app-root',
 templateUrl: './app.component.html',
 styleUrls: ['./app.component.css']
 })
export class AppComponent {

constructor(private mainservice: MainService, private sharedService: 
SharedService) { 
   //doSomething
 }

为什么一个需要@Injectable() 而另一个不需要

我看过一些关于 DI 的视频并浏览了一些文章,但仍然很难理解我们应该在哪里使用这个装饰器以及为什么。 有人可以解释这个装饰器的目的,或者粘贴链接到解释得很好的来源。

是的,你是对的,我们可以在没有 Injectable() 装饰器的情况下创建服务。 那我们为什么需要这个?

可注入装饰器或任何其他角度/自定义装饰器生成元数据。 注入服务需要一种特殊的元数据(设计:参数类型)。

没有依赖项并且没有 Injectable:

 @Component({ selector: 'ponyracer-app', template: '<h1>PonyRacer</h1>' }) export class PonyRacerAppComponent { constructor(private appService: AppService) { console.log(appService); } } // without Injectable export class AppService { constructor() { console.log('new app service'); } }

这是 javascript 为组件和服务生成的代码。 您可以看到组件生成的代码和服务生成的代码之间的一些区别。

Angular 在这里添加了一些元数据,因为我们使用了组件装饰器。 一个元数据是关于 appService 的。 但是服务代码没有元数据。

 var AppService = (function () { function AppService() { console.log('new app service'); } return AppService; }()); exports.AppService = AppService; var PonyRacerAppComponent = (function() { function PonyRacerAppComponent(appService) { this.appService = appService; console.log(appService); } PonyRacerAppComponent = __decorate([ core_1.Component({ selector: 'ponyracer-app', template: '<h1>PonyRacer</h1>' }), __metadata('design:paramtypes', [app_service_1.AppService]) ], PonyRacerAppComponent); return PonyRacerAppComponent; }());

那么如果我们的服务依赖于像下面这样的 HttpService 之类的其他服务会发生什么?

 export class AppService { constructor(http: HttpService) { console.log(http); } }

这是为服务生成的代码:

 var AppService = (function() { function AppService(http) { console.log(http); } return AppService; }()); exports.AppService = AppService;

Angular 未提供有关 http 变量的信息,当我们运行此代码时,它会给出类似错误:无法解析 AppService 的所有参数:(?)。 .

现在,如果此服务使用 Injectable() 或任何自定义的空装饰器进行装饰,它将为 http 服务元数据创建引用。

 var AppService = (function() { function AppService(http) { console.log(http); } AppService = __decorate([ core_1.Injectable(), __metadata('design:paramtypes', [http_service_1.HttpService]) ], AppService); return AppService; }()); exports.AppService = AppService;

来自文档: https://angular.io/guide/dependency-injection


通过在以下三个位置之一设置元数据值,您可以在应用程序的不同级别为提供程序配置注入器:

  • 在服务本身的 @Injectable() 装饰器中。

  • 在 NgModule 的 @NgModule() 装饰器中。

  • 在组件的 @Component() 装饰器中。

@Injectable() 装饰器具有 providedIn 元数据选项,您可以在其中使用根注入器或特定 NgModule 的注入器指定装饰服务 class 的提供者。

要将 class 与 DI 一起使用,它必须具有提供程序,而 @Injectable() 装饰器是注册提供程序的一种方式。


如果您使用@Injectable() ,则不必将 MyServiceClass 显式添加到模块或组件的 providers 属性中。

总之, @Injectable() 不是强制性的,因为它只是定义提供者的另一种方式。 同样,如果您将@Injectable({providedIn: 'root'})添加到您的服务中,则无需将服务添加到任何模块或组件的任何providers属性中。

小例子

假设你有一个组件:

@Component({
  selector: "service-tester",
  templateUrl: "./service-tester.component.html",
  styleUrls: ["./service-tester.component.css"],
  providers: [ProvidedByComponentService]
})
export class ServiceTesterComponent {
  constructor(
    public componentService: ProvidedByComponentService,
    public rootService: ProvidedByRootService
  ) {}
}

它注入两种服务:一种在根级别,另一种在组件中提供。

如果我们查看我们的服务,它们会做同样的事情:

export class ProvidedByComponentService {
  public clickCount: BehaviorSubject<number>;
  constructor() {
    this.clickCount = new BehaviorSubject<number>(0);
  }

  increment() {
    this.clickCount.next(this.clickCount.value + 1);
  }
}

唯一的区别是根中提供的服务有一个装饰器:

@Injectable({ providedIn: "root" })
export class ProvidedByRootService {
 // same as previous service, code omitted for brevity.
}

现在,这些服务之间的唯一区别是在将新的<service-tester>组件添加到模板时,提供者将创建多少个实例。

  • ProvideByComponentService:将为每个 ServiceTesterComponent 创建一个新实例
  • ProvideByRootService:只会创建一个实例。

这意味着当在ProvidedByRootService中增加 count 时,它将在每个ServiceTesterComponent中增加,而在ProvidedByComponentService中的 count 只会在该组件内本地增加

这允许您以灵活的方式声明提供程序,具体取决于服务是否可以是 singleton。

服务,例如 API 服务是可以从根目录中提供的服务的很好示例,因为它们通常不持有任何 state,或持有与整个应用程序相关的 state。

Stackblitz 下面的例子:

https://stackblitz.com/edit/angular-ivy-jboftq?file=src/app/service-tester/service-tester.component.ts

如果您使用@Injectable()装饰器通过将provideIn设置为root值来装饰服务,它将在整个应用程序中创建 singleton 实例,并在您注入的所有组件中使用相同的实例。 例如:

class HomeComponent {
   constructor(private userService: UserService) {}
}

请参考链接: https://angular.io/guide/dependency-injection#injector-hierarchy-and-service-instances

Injectable() 装饰器用于将其他服务或对象注入到您的服务中。

如果您的服务没有任何依赖项,那么您不需要添加 Injectable() 装饰器。 但是,如果您的服务有一些依赖项,那么您必须添加 Injectable() 装饰器,否则 angular 将无法在您的服务中注入依赖项,并且会引发运行时错误。

您有两个服务,一个带有 Injectable() 装饰器,另一个没有 Injectable()。

您会注意到 MainCreditService 具有 Injectable() 装饰器,并且它在构造函数中具有一些依赖关系,即私有 http: Http,私有配置:Config。 但是 SharedService 没有 Injectable() 装饰器,它也没有依赖项。

export class SharedService {
   //do something
}

   @Injectable()
   export class MainCreditService {

     constructor(private http: Http, private config: Config) {
     this.config.EndPoints.subscribe(endpointObj =>  {
        this.environment = endpointObj;
    });

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM