@Injectable() 装饰器和提供者数组

@Injectable() decorator and providers array

@Injectable() 装饰器中 "root" 中提供的服务是否仍必须位于模块的提供程序数组中?

Angular documentation 并没有真正给我答案,或者我不太明白。

在我的核心文件夹中,我有一个在根目录中提供的身份验证服务。我不想在应用程序模块中导入我的核心模块,以便使用提供的所有服务和组件。

我是否必须在模块的 providers 数组中额外设置服务,或者已经使用装饰器在根级别提供它就足够了吗?

您提供的 link 中的要点都是注册服务的不同方法,从最不具体到最具体。

特定于应用程序 - 使用 @Injectable({ providedIn: 'root' })

When you provide the service at the root level, Angular creates a single, shared instance of HeroService and injects it into any class that asks for it. 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.

模块特定 - 在模块提供商中注册

When you register a provider with a specific NgModule, the same instance of a service is available to all components in that NgModule. To register at this level, use the providers property of the @NgModule() decorator,

组件特定 - 在组件中注册

When you register a provider at the component level, you get a new instance of the service with each new instance of that component. At the component level, register a service provider in the providers property of the @Component() metadata.

以上所有引用均来自官方Introduction to services and dependency injection页面

  • 如果你只有一个模块,那么前两种方法是等价的,只需要使用一种方法即可。使用 @Injectable - 默认的 CLI 方法更容易。
  • 如果要在多个模块之间共享服务实例,使用第一种方法。
  • 如果您希望每个独立模块一个实例,请使用第二种方法。
  • 如果您想与除一个组件之外的所有组件共享一个应用程序范围的实例,那么除了对一个异常组件使用第三种方法之外,还可以使用第一种方法。

我认为大多数用例都属于前两种方法。

正在注册特定于模块的服务

提示:只需使用 providedIn: 'root'。如果由于 tree shaking 而未使用,则不会为模块编译未使用的服务。声明特定于模块的服务似乎是多余的,而且正如我们将要看到的那样,可能会导致问题。

有两种方法可以注册模块特定的服务 - 从模块或服务。

模块

@NgModule({
  providers: [MyService]
})
export class MyModule {}

服务

@Injectable({ providedIn: MyModule })

后者是官方推荐的方法。声明 providers 数组是早期的遗留问题。

来自 the docs:

The example above shows the preferred way to provide a service in a module. This method is preferred because it enables tree-shaking of the service if nothing injects it. If it's not possible to specify in the service which module should provide it, you can also declare a provider for the service within the module

为什么你应该只使用 providedIn: 'root'

所以我们看到这种方法是 tree-shakeable 的。到目前为止,一切都很好。但是,如果您只是尝试导入与声明使用客户端的组件相同的模块,那么您最终会得到循环引用。

采用此设置:

我的模块

declarations: [
  MyComponent
]

我的服务

@Injectable({ providedIn: MyModule })

我的组件

constructor(private myService: MyService) {}
  • 我的服务导入我的模块
  • 我的模块导入我的组件
  • 我的组件导入我的服务

存在循环依赖。

解决方法是创建一个服务模块并将其导入到您的模块中。

我的模块

imports: [
  MyModuleServices
],
declarations: [
  MyComponent
]

我的模块服务


我的服务

@Injectable({ providedIn: MyModuleServices })

我的组件

constructor(private myService: MyService) {}

这是一个非常啰嗦的替代方法,可以简单地使用 providedIn: 'root' 并让 tree shaking 来完成工作。

Does a service that is provided in "root" within the @Injectable() decorator still have to be in the providers array of the module?

不!

当您使用 Angular CLI 创建服务时,我相信默认情况下会添加 providedIn: "root",因此从技术上讲,您永远不需要将自己的服务之一添加到提供者数组中.

Do I have to additionally set up the service in the providers array of the module, or is it enough that it is already provided at root level using the decorator?

使用装饰器在根级别提供它就足够了。

但是,并非我创建的所有服务都旨在面向全球。当我创建将在单个屏幕或子模块之间共享的服务时,我将配置对象删除到 @Injectable() 元数据的情况并不少见——而不是在整个 application.In 这种情况下我将不得不将该服务添加到使用它的各个组件或子模块声明的提供程序数组中。