【问题标题】:Why do we use @Injectable() decorator , when we can actually inject services without using the decorator as well?当我们实际上可以在不使用装饰器的情况下注入服务时,为什么要使用 @Injectable() 装饰器?
【发布时间】:2021-03-12 09:28:08
【问题描述】:

我正在做一个项目,遇到了一个没有 @Injectable() 装饰器的服务,并且工作正常。到现在为止,我的印象是,如果我们想实现 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 的视频并浏览了一些文章,但仍然很难理解我们应该在哪里使用这个装饰器以及为什么。有人可以解释一下这个装饰器的目的吗,或者粘贴链接到解释得很好的来源。

【问题讨论】:

    标签: angular dependency-injection decorator angular-services


    【解决方案1】:

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

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

    没有依赖和没有 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 变量的信息,当我们运行此代码时,它会给出类似 Error: Can't resolve all parameters for 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;

    【讨论】:

      【解决方案2】:

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


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

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

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

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

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

      要通过 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.
      }
      

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

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

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

      这允许您根据服务是否可以是单例,以灵活的方式声明提供者。

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

      Stackblitz 下面的例子:

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

      【讨论】:

        【解决方案3】:

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

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

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

        【讨论】:

        • 非常感谢......但是我可以在没有 @injectable() 装饰器的情况下做同样的事情,对吧?......我已经删除了 @injectable() 装饰器,只是将我的服务添加到了提供者。 .一切正常。那么当我即使没有它也能够执行完全相同的功能时,这个@injectable 的目的是什么
        【解决方案4】:

        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;
            });
        

        【讨论】:

          猜你喜欢
          • 2018-02-19
          • 2012-03-22
          • 2022-11-23
          • 2010-09-21
          • 2018-04-17
          • 2021-07-03
          • 2011-01-02
          • 2019-01-18
          • 1970-01-01
          相关资源
          最近更新 更多