在 Angular 6 中生成服务时,providedIn 与 Injectable 装饰器的目的是什么?

What is the purpose of providedIn with the Injectable decorator when generating Services in Angular 6?

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

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

providedIn 到底是做什么的?我假设这使服务像 'global' 类型的单例服务一样可用于整个应用程序,但是,在 AppModule 的提供程序数组中声明此类服务不是更清晰吗?

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

来自Docs

The service itself is a class that the CLI generated and that's decorated with @Injectable. By default, this decorator is configured with a providedIn property, which creates a provider for the service. In this case, providedIn: 'root' specifies that the service should be provided in the root injector.

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

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

根据 Documentation:

Registering the provider in the @Injectable() metadata also allows Angular to optimize an app by removing the service from the compiled app if it isn't used.

providedIn: 'root' 是自 Angular 6:

以来提供服务最简单、最有效的方式
  1. 该服务将作为单例在整个应用程序范围内可用,无需将其添加到模块的提供程序数组(如 Angular <= 5)。
  2. 如果服务仅在延迟加载的模块中使用,它将与该模块一起延迟加载
  3. 如果从未使用过,它将不会包含在构建中(摇树)。

有关更多信息,请考虑阅读 documentation and NgModule FAQs

顺便说一句:

  1. 如果您不想要应用程序范围的单例,请改用提供者的组件数组。
  2. 如果您想限制范围,使其他开发人员永远不会在特定模块之外使用您的服务,请改用 NgModule 的 providers 数组。

来自文档

What is Injectable decorator?

将 class 标记为可供注入器创建。

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

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

服务本身是 CLI 生成的 class,并用 @Injectable() 修饰。

What exactly does providedIn do?

通过将其与@NgModule 或其他 InjectorType 相关联,或通过指定应在 'root' 注入器中提供此注入器来确定哪些注入器将提供注入器,这将是应用程序级大多数应用程序中的注入器。

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

providedIn: 'root'

当您在根级别提供服务时,Angular 会创建一个单一的共享服务实例,并将其注入到任何需要它的 class 中。在 @Injectable() 元数据中注册提供程序还允许 Angular 通过从编译的应用程序中删除未使用的服务来优化应用程序。

providedIn: Module

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

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

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

此方法是首选,因为它启用了 Tree-shaking(Tree shaking 是构建过程中的一个步骤,它从代码库中删除未使用的代码) 的服务,如果没有注入它。

如果无法在服务中指定由哪个模块提供,您也可以在模块内声明服务提供者:

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

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

请参阅

的精彩解释

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

如果你只是使用 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 Guides

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.