【问题标题】:Angular 4 Core Module, why there are two provider arraysAngular 4 Core Module,为什么有两个提供者数组
【发布时间】:2017-07-20 13:45:59
【问题描述】:

我正在查看有关 NgModule 的官方 Angular 文档 (https://angular.io/guide/ngmodule#configure-core-services-with-coremoduleforroot) 在页面末尾提供的示例中,有文件 core.module.ts

import { ModuleWithProviders, NgModule, Optional, SkipSelf }       from '@angular/core';
import { CommonModule }      from '@angular/common';
import { TitleComponent }    from './title.component';
import { UserService }       from './user.service';
import { UserServiceConfig } from './user.service';

@NgModule({
  imports:      [ CommonModule ],
  declarations: [ TitleComponent ],
  exports:      [ TitleComponent ],
  providers:    [ UserService ]
})
export class CoreModule {

  constructor (@Optional() @SkipSelf() parentModule: CoreModule) {
    if (parentModule) {
      throw new Error(
        'CoreModule is already loaded. Import it in the AppModule only');
    }
  }

  static forRoot(config: UserServiceConfig): ModuleWithProviders {
    return {
      ngModule: CoreModule,
      providers: [
        {provide: UserServiceConfig, useValue: config }
      ]
    };
  }
}

这是一个核心模块,通过使用构造函数和 forRoot 方法 我们确定核心模块只导入一次,并且我们只有一个所提供服务的实例。

  • @NgModule({...}) 中的 providers 数组和 forRoot 方法中的 providers 数组有什么区别?
  • 我应该在 @NgModel 部分还是在核心模块中提供我的单例服务?

【问题讨论】:

    标签: angular


    【解决方案1】:

    forRoot 用于创建单例。

    您应该只在根级别 (AppModule) 调用 forRoot()

    采用这种结构:

    @NgModule({
        imports: [
            CommonModule
        ],
        declarations: [],
        exports: [],
        providers: []
    
    })
    export class I18nModule {
        static forRoot() {
            return {
                ngModule: I18nModule,
                providers: [I18nService, UserConfig]
            };
        }
    }
    

    您必须像这样在AppModule 中导入I18nModule(只是一个示例):

    I18nModule.forRoot()
    

    在其余的导入中也是这样:

    imports: [I18nModule]
    

    基本上,在 AppModule 导入中,您正在实例化 forRoot() 导入模块的 providers 中的服务的单例。这允许您始终引用同一个实例,而不是拥有同一个服务的多个实例。

    为什么要使用 2 个提供者数组:

    在您的问题中,第一个数组可能完全是空的,将您的所有提供程序添加到您的 forRoot() providers 数组中。所以你可以:

    @NgModule({
      imports:      [ CommonModule ],
      declarations: [ TitleComponent ],
      exports:      [ TitleComponent ],
      providers:    []
    })
    export class CoreModule {
    
      constructor (@Optional() @SkipSelf() parentModule: CoreModule) {
        if (parentModule) {
          throw new Error(
            'CoreModule is already loaded. Import it in the AppModule only');
        }
      }
    
      static forRoot(config: UserServiceConfig): ModuleWithProviders {
        return {
          ngModule: CoreModule,
          providers: [
            UserService,
            {provide: UserServiceConfig, useValue: config }
          ]
        };
      }
    }
    
    • 您应该在您的CoreModule 中提供您的单例服务,并在您的AppModule 中使用forRoot() 导入它
    • 如果您使用的是 forRoot(),请在您的 forRoot 方法中添加您的提供程序

    【讨论】:

    • userServiceConfig 有什么用处?
    • @Moshe 用于处理用户的配置。通常这意味着UserServiceConfig 扩展了UserService 类。检查这个:angular.io/guide/…
    • 这在生产环境中如何工作?当前设置:我正在使用 localstorage 存储 JWT 令牌 - 其中提供用户名、名称等。
    • 当您拥有token后,您可以在UserServiceConfig服务中查询您的BE或API以获取用户信息。如果我可以给您我的观点,请不要遵循本地存储方法,它会永久存储令牌和访问信息。恕我直言,饼干或季节更好..
    • 令牌在 3 天后过期,我的网站现在不需要更多的安全性。还是不明白 Angular 的文档:/
    【解决方案2】:

    这是一个核心模块,通过使用构造函数和 forRoot 方法,我们可以确保核心模块只被导入一次,并且我们只提供了一个服务实例。

    从技术上讲,它不能确保它只有一次。如果有重复,构造函数会抛出错误。解决重复错误成为开发人员的责任,有时这可能很困难。

    @NgModule({...}) 中的 providers 数组和 forRoot 方法中的 providers 数组有什么区别?

    @NgModule 创建附加到 CoreModule 类的元数据。 Angular 使用这个元数据来加载模块。 forRoot 是一个将元数据作为数组返回的函数。这意味着您无需借助装饰器来格式化该数组,而必须手动进行。

    forRoot 中的providers 声明一个依赖符号与一个值相关联。该值作为参数传递给函数。

    您实际上可以使用@NgModule 执行此操作并获得相同的结果。

     let config = new UserServiceConfig(); // <-- create instance here.
    
     @NgModule({
         imports:      [ CommonModule ],
         declarations: [ TitleComponent ],
         exports:      [ TitleComponent ],
         providers:    [ 
             UserService,
             {provide: UserServiceConfig, useValue: config }
         ]
     })
    

    上面和forRoot()做同样的事情,但它引入了两个问题。

    • 以上代码可以多次执行
    • 模块如何知道它需要什么配置?

    我应该在@NgModel 部分还是在核心模块中提供我的单例服务?

    我想说 Angular 中没有单例。那是因为依赖注入树不支持提供者的这种分类。为了解决这个限制,我们添加了这个forRoot 技巧来为一个特定的导入声明一个提供者仅一次

    这是理解其工作原理的关键。我们不会更改所提供的内容。我们正在更改导入的内容。

    这意味着我们只需要一个模块导入这个服务。假设进行导入的模块只加载一次。因此,这会创建该提供程序的单例。

    重要的是你的ma​​in模块在它的imports部分调用forRoot(),并且这个函数只使用一次。

    您阅读的教程使用此技术仅创建一个UserServiceConfig 类型的配置对象的一个 提供程序。然后,您可以将此类型注入到您的其他对象中。

    【讨论】:

      猜你喜欢
      • 2022-10-15
      • 1970-01-01
      • 1970-01-01
      • 2018-01-16
      • 2018-04-03
      • 2018-03-16
      • 2018-07-05
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多