在Angular 6中生成服务时,为din提供可注入装饰器的目的是什么?

当在Angular CLI中生成服务时,它会为Injectable装饰器添加一个'provided in'属性(默认值为'root')的额外元数据。

@Injectable({
providedIn: 'root',
})

providedIn到底做什么?我假设这是使服务可用的像一个“全局”类型的单例服务的整个应用程序,然而,不会更干净地在AppModule的提供者数组中声明这样的服务?

100286 次浏览

如果使用providedIn,可注入对象被注册为模块的提供者,而不需要将其添加到模块的提供者中。

Docs

服务本身是CLI生成的一个类 用@Injectable装饰。默认情况下,配置了这个装饰器 使用providedIn属性,该属性为服务创建提供者。 在这种情况下,providedIn: 'root'指定服务应该是 在根注入器中提供

providedIn告诉Angular,根注入器负责创建你的服务的实例。以这种方式提供的服务将自动提供给整个应用程序,不需要在任何模块中列出。

服务类可以充当它们自己的提供者,这就是为什么在@Injectable装饰器中定义它们就是你所需要的全部注册。

根据Documentation:

在@Injectable()元数据中注册提供者也允许 Angular通过从编译后的

providedIn: 'root'是自Angular 6以来最简单、最有效的提供服务的方法:

  1. 该服务将作为单例在整个应用范围内可用,不需要将其添加到模块的providers数组中(如Angular <= 5)。
  2. 如果服务只在一个惰性加载的模块中使用,那么该模块也会被惰性加载
  3. 如果它从未被使用过,它将不会包含在构建中(摇树)。

欲了解更多信息,请考虑阅读文档NgModule常见问题 . c

顺便说一句:

  1. 如果你不想要应用范围的单例,可以使用组件的提供者数组。
  2. 如果你想限制作用域,让其他开发人员无法在特定模块之外使用你的服务,可以使用NgModule的providers数组。

从文档

什么是可注入装饰器?

将一个类标记为可用于Injector创建。

import { Injectable } from '@angular/core';


@Injectable({
providedIn: 'root',
})
export class UserService {
}

服务本身是CLI生成的一个类,并使用@Injectable()进行装饰。

providedIn到底做什么?

通过将可注入对象与@NgModule或其他InjectorType关联,或者指定该可注入对象应该在“根”注入器中提供,这将是大多数应用程序中的应用级注入器。

providedIn: Type<any> | 'root' | null

providedIn:“根”

当你在根级提供服务时,Angular会创建一个单独的、共享的服务实例,并将它注入到任何需要它的类中。在@Injectable()元数据中注册提供商还可以让Angular通过在编译后的应用中删除不使用的服务来优化应用。

providedIn:模块

也可以在特定的@NgModule中指定一个服务。例如,如果您不希望某个服务对应用程序可用,除非它们导入您创建的模块,您可以指定该服务应该在模块中提供

import { Injectable } from '@angular/core';
import { UserModule } from './user.module';


@Injectable({
providedIn: UserModule,
})
export class UserService {
}

此方法是首选的,因为它可以在没有注入服务时启用摇树(< em >树摇晃是构建过程中的一个步骤,从代码库中删除未使用的代码)。

如果不能在服务中指定应该由哪个模块提供该服务,你也可以在模块中声明该服务的提供者:

import { NgModule } from '@angular/core';
import { UserService } from './user.service';


@NgModule({
providers: [UserService],
})
export class UserModule {
}

参见@Nipuna的精彩解释,

我想通过添加例子来扩展它。

如果你只是使用了没有providedin属性的Injectable装饰器,比如,

@Injectable()

那么你必须在各自模块的providers数组中写入服务的名称。

像这样;

data.service.ts↴

import { Injectable } from '@angular/core';


@Injectable()
export class DataService {
constructor() {}


// Code . . .
}

app.module.ts↴

import { AppComponent } from './app.component';
import { DataService } from './core/data.service';


@NgModule({
declarations: [AppComponent],
providers: [DataService],    // ⟵ LOOK HERE WE PROVIDED IT
imports: [...],
bootstrap: [AppComponent],
})
export class AppModule {}

但是,如果你使用providedIn: 'root',像这样:

data.service.ts↴

import { Injectable } from '@angular/core';


@Injectable({
providedIn: 'root',
})
export class DataService {
constructor() {}


// Code . . .
}

然后我们的模块看起来像这样:

app.module.ts↴

import { AppComponent } from './app.component';
import { DataService } from './core/data.service';


@NgModule({
declarations: [AppComponent],
providers: [],
imports: [...],
bootstrap: [AppComponent],
})
export class AppModule {}

看,这次我没有在providers数组中添加DataService,因为它不需要。

良好的实践

这可能会派上用场,从角指南

在服务的@Injectable装饰器中提供一个带有应用根注入器的服务。

Angular的注入器是分层的。

为什么?当你向根注入器提供服务时,该服务的实例是共享的,并且在每个需要该服务的类中都可用。当服务共享方法或状态时,这是理想的。

为什么?当你在服务的@Injectable装饰器中注册一个服务时,优化工具(比如Angular CLI的产品构建中使用的那些工具)会执行摇树操作,并移除应用中不使用的服务。

当两个不同的组件需要一个服务的不同实例时,这是不理想的。在这种情况下,最好在组件级别提供需要新的单独实例的服务。

这是一个最新的回复,是Angular 9+的最新版本(后ivy版本),应该在2022年是正确的。


博士TL;:这一切都是关于控制你的服务将创建多少个实例,以及创建后它们应该在哪里可用。


术语:

  • 可注射的 -任何用@Injectable装饰的类,例如服务。

  • 注射器——一个Angular类,能够向它下面的类提供可注入对象。(包括所有组件和模块。)

  • 注射器/水平范围 -生活在"下面"的所有类实例的范围;特定的注入器。

  • 注入器层次结构 -注入器作用域的优先级树,按platform -> root -> module -> component顺序组织。

  • Injectable is 提供——一个Injectable的实例将被提供给这个特定注入器级别以下的类,只要它们请求它。

  • 可注入的是注射——类构造函数已经请求提供服务的某个实例,因此Angular将尝试给它一个能在注入器层次结构中找到的最近的实例"

  • tree-shaking——这是一个自动发生的优化,这要感谢Angular编译器。当它检测到某些代码没有被使用时,该代码将从应用程序的最终编译(或给定惰性加载模块的编译)中删除。

你应该已经知道的其他术语:类、实例、模块、组件、惰性加载/急切加载模块


问:providedIn到底做什么?

这是一个决定哪些注入器应该提供你的Injectable的设置。

让我们假设我们创建了一个名为MyService的注入对象,并执行所有的选项的操作。

providedIn: Type<any> | 'root' | 'platform' | 'any' | null

providedIn: 'platform'

Angular会创建一个MyService的共享实例,并提供给页面上所有的Angular 应用程序实例。(这只与高级用例相关,如果您使用微前端架构的话。)

providedIn: 'root'

Angular将创建一个MyService的共享实例,并将其提供给应用程序中的所有类。

providedIn: 'any'

Angular将创建一个MyService的共享实例,并将它提供给所有在急切加载的模块中类。

然而,每个延迟加载模块将提供它自己的、新的、单独的MyService实例(然后只在该模块内部的类中可用)。

providedIn: MyModule

Angular只会在加载MyModule时创建一个MyService实例。

如果MyModule急切地加载,从现在开始,该实例将对所有其他急切加载的模块可用。(注意,这实际上与providedIn: 'root'相同。)

然而,如果MyModule懒加载,则该实例将仅为MyModule内的类提供,无论何时加载它。

providedIn: MyComponent

每当MyComponent被实例化时,Angular都会创建一个新的MyService实例。

这个MyService实例只会提供给那个特定的MyComponent 实例的后代,并且会在组件实例被销毁时被销毁。(注意,这意味着该组件每次渲染时都会创建一个新的MyService。)

providedIn: null

MyService只能通过被添加到特定模块或组件的providers数组中来实例化。

无论何时该模块/组件被实例化,它都将创建一个MyService的新实例,并仅在其特定的作用域中提供该实例。(参见下面providers数组的完整描述。)


问:providers数组做什么?

任何注入器都可以用providers数组来设置:

@NgModule({
providers: [MyService],
})


@Component({
providers: [MyService],
})

所有注入对象都可以被添加到providers数组中,即它们的providedIn设置的不管

MyService添加到providers数组将导致注入器创建并为其作用域中的类提供一个完全独立的实例。(作用域与上面providedIn: MyModuleprovidedIn: MyComponent示例中描述的完全相同。)

这种提供方法不支持摇树。该服务将始终包含在编译中,即使没有人使用它。(见下面的摇树笔记。)

问:为什么我要同时使用providers数组 providedIn ?

例如,如果MyServiceprovidedIn: 'root',并且已经有一个共享实例,但是你希望你的模块/组件有它自己的、单独的实例。


其他说明:

问:providedIn/providers设置如何影响摇树?

如果一个可注入的providedIn配置没有被它指定的注入器作用域中的任何类(急切或惰性加载)注入,它将被摇树。

然而,某些模块/组件中的可注入赋值给providers数组永远不会被摇树,即使它没有被注入到任何地方。

为了使摇树最有效,你应该总是在providers数组上使用providedIn

问:如果我认为在AppModule中使用providers数组看起来更干净,为什么我要使用providedIn: 'root' ?

如上所述,这两个方法的主要区别在于,providedIn支持摇树,而providers array不支持摇树。

除此之外,这是一个架构决策:如果我们直接在可注射文件中设置providedIn,则可注射的拥有决定应该如何提供它。区分谁拥有合同对于必须在数百个模块之间合作的大型应用程序和团队具有重要意义。

问:在AppComponentAppModule中设置providers: [MyService]数组有区别吗?

是的。如果你在AppModule而不是AppComponent中做,MyService将在惰性加载模块只有中提供。

(这是因为惰性加载模块依赖于Router,它在AppModule中被导入,比AppComponent高一个注入器范围。)

简单. .

providedIn :'root'为整个应用程序创建一个实例,而不需要从任何NgModule提供它。只要通过@Injectable装饰器在服务中声明它。

如果您希望为任何组件拥有该服务的一个新实例,那么可以通过组件的提供程序声明它。这将为该组件及其子组件(如果有的话)创建另一个新实例。因此,您可以有一个具有全局作用域的实例和一个组件的另一个实例。