Angular forRoot 详解

Photo by Jaanus Jagomägi on Unsplash

在Angular中,常常会看到在 AppModule 中调用某个模块的 forRoot 方法。

其中最常见的应该就是 RouterModule.forRoot()。但是 forRoot 的作用是什

么?什么时候要自己编写 forRoot?读完本文,相信你会有一个答案。

forRoot 解决的问题

forRoot 方法主要是为了解决依赖注入(DI)实例生成的问题。在 《 Angular

模块基础教程 》中曾经提到,惰性加载的模块会单独生成一个依赖注入的实

例。这在某些场景下可能不是你想要的。

一个 CountService

假如你想创建一个全局计数的 CountService,然后你又采用惰性加载的方式来

加载包含此 service 的模块,那么由于惰性加载的模块拥有的 CountService 实

例,和正常加载的模块所拥有的 CountService 实例不相同。那么就可能导致

计数的值出现不一致。Chris House 写了这样一个 Demo ,可以帮助我们更好

地理解这个场景。

可以清楚的看到惰性加载的模块和主模块依赖注入的并不是同一个实例,

因此,累计计数的结果不一致。在这种场景下,就要使用 forRoot 来生成全局

唯一的 service。

forRoot 如何写

若你想让某个 CoreModule 中的 services ,够在应用程序的全局生命周期中使

用。那么可以给 CoreModule 添加一个名为 forRoot 的静态方法。

并返回一个 ModuleWithProviders 对象。

import { NgModule, ModuleWithProviders } from '@angular/core';
import { SomeService } from './some.service';

@NgModule({
  ...
})
export class CoreModule {
  static forRoot(): ModuleWithProviders {
    return {
      ngModule: CoreModule,
      providers: [ SomeService ]
    };
  }
}

然后你只要在应用的 AppModule 中 imports 这个 CoreModule,

就能在应用生命周期的全局,无论子模块是否是惰性加载,

共用这个全局的 service 实例。

除此之外!

当然对于不是惰性加载的模块,多次重复导入相同的服务同样也是不可取的。

这是因为,Angular 通过维护一个 root 的根注入器完成全局级别的依赖注入,

所有 AppModule 中 imports 的模块中自带的服务都会通过这个根注入器完成

实例化,而依据就是服务的 token。因此,当我们在多个子模块/特性模块中导

入了相同的服务(token 相同),就会导致这个服务被注册许多次。尽管由于

token 相同,最后这个服务实例会被覆盖到只剩一个,但是在应用启动期间这

样的低效操作也是无法容忍的。

最佳实践

对于本身包含一系列服务的模块,我们最好把这些服务放在 forRoot 方法中,

并在 forChild 方法中单纯提供组件或者指令等其他元数据。这样,不仅可以减

少 Bug,还能并减少应用的启动时间。

相关阅读

Angular 模块漫谈

Angular 路由快速教程

深入理解 Angular 结构型指令

参考链接

When to use Angular’s forRoot() method

使用 forRoot() 幫助 SharedModule 提供單一實例服務

Defining Providers in Shared Modules in Angular

Understanding provider scope in Angular

发表评论

电子邮件地址不会被公开。 必填项已用*标注