Angular forRoot 详解

Photo by Jaanus Jagomägi on Unsplash

声明

此文是我 Angular 系列文章中的一篇。在 Angular 系列文章中,我尽可能的用简单的语言对 Angular 的许多概念进行总结,分析了一些 API 的设计的原因,希望这些东西能对读者有所帮助。

正文

在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 对象。

然后你只要在应用的 AppModule 中 imports 这个 CoreModule,就能在应用生命周期的全局,无论子模块是否是惰性加载,共用这个全局 service 实例。

除此之外!

当然对于不是惰性加载的模块,多次重复导入相同的服务同样也是不可取的。这是因为,Angular 通过维护一个 root 的根注入器完成全局级别的依赖注入,所有 AppModule 中 imports 的模块中自带的服务都会通过这个根注入器完成实例化,而依据就是服务的 token。

因此,当我们在多个子模块/特性模块中导入了相同的服务(token 相同),就会导致这个服务被注册许多次。尽管由于 token 相同,最后这个服务实例会被覆盖到只剩一个,但是在应用启动期间这样低效的操作也是无法容忍的。

最佳实践

对于本身包含一系列服务的模块,我们最好把它们放在 forRoot 方法中,并在 forChild 方法中单纯提供组件或者指令等其他元数据。这样,不仅可以减少 Bug,还能并减少应用的启动时间。

相关阅读

我在 Angular 模块漫谈 中对导入第三方模块的方法进行了全面的总结。这里也再次提到了 forRoot 与 forChild 的区别。

高级教程

Zone.js 入门

Zone.js 源码初探

参考链接

When to use Angular’s forRoot() method

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

Defining Providers in Shared Modules in Angular

Understanding provider scope in Angular

发表评论

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