当在Angular CLI中生成服务时,它会为Injectable装饰器添加一个'provided in'属性(默认值为'root')的额外元数据。
@Injectable({
providedIn: 'root',
})
providedIn到底做什么?我假设这是使服务可用的像一个“全局”类型的单例服务的整个应用程序,然而,不会更干净地在AppModule的提供者数组中声明这样的服务?
当在Angular CLI中生成服务时,它会为Injectable装饰器添加一个'provided in'属性(默认值为'root')的额外元数据。
@Injectable({
providedIn: 'root',
})
providedIn到底做什么?我假设这是使服务可用的像一个“全局”类型的单例服务的整个应用程序,然而,不会更干净地在AppModule的提供者数组中声明这样的服务?
如果使用providedIn,可注入对象被注册为模块的提供者,而不需要将其添加到模块的提供者中。
从文档
服务本身是CLI生成的一个类 用@Injectable装饰。默认情况下,配置了这个装饰器 使用providedIn属性,该属性为服务创建提供者。 在这种情况下,providedIn: 'root'指定服务应该是 在根注入器中提供。
providedIn告诉Angular,根注入器负责创建你的服务的实例。以这种方式提供的服务将自动提供给整个应用程序,不需要在任何模块中列出。
服务类可以充当它们自己的提供者,这就是为什么在@Injectable装饰器中定义它们就是你所需要的全部注册。
自Angular 6以来,providedIn: 'root'是最简单、最有效的提供服务的方法:
该服务将作为单例在整个应用范围内可用,不需要将其添加到模块的providers数组中(如Angular <= 5)。 如果服务只在一个惰性加载的模块中使用,那么该模块也会被惰性加载 如果它从未被使用过,它将不会包含在构建中(摇树)。
欲了解更多信息,请阅读文档和NgModule faq
Btw:
如果你不想要应用范围的单例,可以使用组件的提供者数组。 如果你想限制作用域,让其他开发人员无法在特定模块之外使用你的服务,可以使用NgModule的providers数组。
从文档
什么是可注入装饰器?
将一个类标记为可用于Injector创建。
import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root',
})
export class UserService {
}
服务本身是CLI生成的一个类,并使用@Injectable()进行装饰。
providedIn到底做什么?
通过将可注入对象与@NgModule或其他InjectorType关联,或者指定该可注入对象应该在“根”注入器中提供,这将是大多数应用程序中的应用级注入器。
providedIn: Type<any> | 'root' | null
提供:“根”
当你在根级提供服务时,Angular会创建一个单独的、共享的服务实例,并将它注入到任何需要它的类中。在@Injectable()元数据中注册提供商还可以让Angular通过在编译后的应用中删除不使用的服务来优化应用。
提供:模块
也可以在特定的@NgModule中指定一个服务。例如,如果您不希望某个服务对应用程序可用,除非它们导入您创建的模块,您可以指定该服务应该在模块中提供
import { Injectable } from '@angular/core';
import { UserModule } from './user.module';
@Injectable({
providedIn: UserModule,
})
export class UserService {
}
这种方法是首选的,因为如果没有注入服务,它可以对服务进行摇树(摇树是构建过程中的一个步骤,从代码库中删除未使用的代码)。
如果不能在服务中指定应该由哪个模块提供该服务,你也可以在模块中声明该服务的提供者:
import { NgModule } from '@angular/core';
import { UserService } from './user.service';
@NgModule({
providers: [UserService],
})
export class UserModule {
}
@Nipuna的精彩解释,
我想通过添加例子来扩展它。
如果你只使用Injectable装饰器而不使用providedin属性,比如,
@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,因为它不需要。
良好的实践
在Angular指南中,这可能会派上用场
Do provide a service with the app root injector in the @Injectable decorator of the service. Why? The Angular injector is hierarchical. Why? When you provide the service to a root injector, that instance of the service is shared and available in every class that needs the service. This is ideal when a service is sharing methods or state. Why? When you register a service in the @Injectable decorator of the service, optimization tools such as those used by the Angular CLI's production builds can perform tree shaking and remove services that aren't used by your app. Why? This is not ideal when two different components need different instances of a service. In this scenario it would be better to provide the service at the component level that needs the new and separate instance.
这是一个最新的回复,是Angular 9+的最新版本(后ivy版本),应该在2022年是正确的。
TL;DR:这都是关于控制您的服务将创建多少个实例,以及创建后它们应该在哪里可用。
术语:
Injectable - any class decorated with @Injectable, for example a service. injector - an Angular class that is capable of providing Injectables to classes below it. (This includes all components and modules.) injector scope/level - the scope of all class instances that live "below" a specific injector. injector hierarchy - a proritized tree of injector scopes, organized in platform -> root -> module -> component order. Injectable is provided - an instance of the Injectable will be given to classes below this specific injector level, whenever they request it. Injectable is injected - a class constructor has requested to be given some instance of the service, so Angular will try to give it the nearest instance that can be found in the injector hierarchy". tree-shaking - an optimization that happens automatically thanks to the Angular compiler. When it detects that some code is not being used, that code is removed from the final compilation of the app (or compilation of a given lazy-loaded module).
你应该已经知道的其他术语:类、实例、模块、组件、惰性加载/急切加载模块。
问:providedIn到底做什么?
这是一个决定哪些注入器应该提供你的Injectable的设置。
让我们假设我们创建了一个名为MyService的可注入对象,并查看所有选项的功能。
providedIn: Type<any> | 'root' | 'platform' | 'any' | null
提供于:“平台”
Angular会为页面上的所有Angular应用创建并提供一个MyService的共享实例。(这只与高级用例相关,如果您使用微前端架构的话。)
提供:“根”
Angular会创建一个MyService的共享实例,并将它提供给应用中的所有类。
提供:“任何”
Angular会创建一个MyService的共享实例,并将它提供给急切加载模块中的所有类。
但是,每个惰性加载模块将提供它自己的、新的、单独的MyService实例(然后只在该模块内的类中可用)。
providedIn: MyModule
Angular只会在MyModule被加载时创建一个MyService实例。
如果MyModule被急切加载,从现在开始,该实例将对所有其他急切加载的模块可用。(注意,这实际上与providedIn: 'root'相同。)
但是,如果MyModule是惰性加载的,那么这个实例将只提供给MyModule内部的类,无论它何时被加载。
MyComponent providedIn:添加
每当MyComponent被实例化时,Angular都会创建一个新的MyService实例。
这个MyService实例只会提供给特定MyComponent实例的后代,并且会在组件实例被销毁时立即销毁。(注意,这意味着每次呈现这个组件时都会创建一个新的MyService。)
提供:空
MyService只能通过添加到特定模块或组件中的providers数组来实例化。
无论何时该模块/组件被实例化,它都将创建一个新的MyService实例,并仅在其特定的范围内提供该实例。(请参阅下面providers数组的完整描述。)
问:提供商数组做什么?
任何注入器都可以用providers数组来设置:
@NgModule({
providers: [MyService],
})
@Component({
providers: [MyService],
})
所有injectable都可以添加到providers数组中,不管它们的providedIn设置如何。
将MyService添加到providers数组将导致注入器创建并为其作用域中的类提供一个完全独立的MyService实例。(作用域与上面的providedIn: MyModule和providedIn: MyComponent示例中描述的完全相同。)
这种提供方法不支持摇树。该服务将始终包含在编译中,即使没有人使用它。(见下面的摇树笔记。)
问:为什么我要同时使用提供程序数组和providedIn ?
例如,如果MyService是providedIn: 'root',并且已经有一个共享实例,但是你想让你的模块/组件有它自己的、单独的实例。
其他说明:
问:providedIn/providers设置如何影响摇树?
如果一个配置了providedIn的Injectable没有被它指定的注入器作用域中的任何类(主动或惰性加载)注入,它就会被摇树。
然而,分配给某些模块/组件中的providers数组的Injectable永远不会被摇树,即使它没有被注入到任何地方。
为了使摇树更有效,应该始终在providers数组上使用providedIn。
问:为什么我要使用providedIn: 'root'如果我认为使用提供商数组在AppModule看起来更干净?
如上所述,这两个方法的主要区别在于,providedIn支持摇树,而providers数组不支持摇树。
除此之外,这是一个架构决策:如果我们直接在Injectable文件中设置了providedIn, Injectable就拥有如何提供它的决定权。区分谁拥有合同对于必须在数百个模块之间进行合作的大型应用程序和团队具有重要意义。
问:在AppComponent和AppModule中设置提供者:[MyService]数组有区别吗?
是的。只有在AppModule中,而不是AppComponent中,才会在惰性加载模块中提供MyService。
(这是因为惰性加载模块依赖于Router,它被导入到AppModule中,比AppComponent高一个注入器范围。)
简单. .
providedIn:'root'为整个应用程序创建了一个实例,而不需要从任何NgModule中提供它。只需通过@Injectable装饰器在服务中声明即可。
如果您希望为任何组件拥有该服务的一个新实例,那么可以通过组件的提供程序声明它。这将为该组件及其子组件(如果有的话)创建另一个新实例。因此,您可以有一个具有全局作用域的实例和一个组件的另一个实例。