Angular 2 Provider

依赖注入(DI) 是 Angular 2 的核心,在深刻了解DI的工做原理以前,咱们必须先搞清楚 Provider 的概念。html

图片描述

在 Angular 2 中咱们使用 Provider 来描述与 Token 关联的依赖对象的建立方式。Angular 2 中依赖对象的建立方式有四种,它们分别是:json

  • useClasssegmentfault

  • useValueapi

  • useExisting数组

  • useFactoryide

useClass

@Injectable()
export class ApiService {
   constructor(
      public http: Http, 
      public loadingCtrl: LoadingController) {
   }
   ...
}

@NgModule({
  ...
  providers: [
       { provide: ApiService, useClass: ApiService } // 可以使用简洁的语法,即直接使用ApiService
  ]
})
export class CoreModule { }

useValue

{ provide: 'API_URL', useValue: 'http://my.api.com/v1' }

useExisting

{ provide: 'ApiServiceAlias', useExisting: ApiService }

useFactory

export function configFactory(config: AppConfig) {
  return () => config.load();
}

@NgModule({
  ...
  providers: [
       { provide: APP_INITIALIZER, useFactory: configFactory, 
        deps: [AppConfig], multi: true }
  ]
})
export class CoreModule { }

使用 Provider 的正确姿式

1.建立 Token函数

Token 的做用是用来标识依赖对象,Token值多是 Type、InjectionToken、OpaqueToken 类的实例或字符串。一般不推荐使用字符串,由于若是使用字符串存在命名冲突的可能性比较高。在 Angular 4.x 之前的版本咱们通常使用 OpaqueToken 来建立 Token,而在 Angular 4.x 以上的版本版本,推荐使用 InjectionToken 来建立 Token 。详细的内容能够参考, 如何解决 Angular 2 中 Provider 命名冲突this

2.根据实际需求选择依赖对象的建立方式,如 useClass 、useValue、useExisting、useFactoryspa

3.在 NgModule 或 Component 中注册 providers3d

4.使用构造注入的方式,注入与 Token 关联的依赖对象

/**
* 封装Http服务,如在每一个Http的请求头中添加token,相似于Ng1.x中的拦截器
*/
@Injectable() 
export class ApiService {
   constructor(
   // 注入Angular 2 中的Http服务,与Ng1.x的区别:
   // 在Ng1.x中调用Http服务后,返回Promise对象
   // 在Ng2.x中调用Http服务后,返回Observable对象
      public http: Http) { 
   }
   ...
}

/**
* AppModule
*/
@NgModule({
  ...
  providers: [
       { provide: ApiService, useClass: ApiService } // 可以使用简洁的语法,即直接使用ApiService
  ]
})
export class AppModule { }

/**
* 系统首页
*/
@Component({
  selector: 'page-home',
  templateUrl: 'home.html'
})
export class HomePage {
  constructor(
    public apiService: ApiService // 使用构造注入的方式,注入ApiService的实例对象
  ) { }
  
  ngOnInit(): void {
    this.apiService.get(HOME_URL) // 获取首页相关的数据
    .map(res => res.json()) // 返回的res对象是Response类型的实例
    .subscribe(result => {
      ...
    })
  }
}

我有话说

1.当DI解析 Providers 时,都会对提供的每一个 provider 进行规范化处理,即转换成标准的形式。

function _normalizeProviders(providers: Provider[], res: Provider[]): Provider[] {
  providers.forEach(b => {
    if (b instanceof Type) { // 支持简洁的语法,转换为标准格式
      res.push({provide: b, useClass: b});
    } else if (b && typeof b == 'object' && (b as any).provide !== undefined) {
      res.push(b as NormalizedProvider);
    } else if (b instanceof Array) {
      _normalizeProviders(b, res); // 若是是数组,进行递归处理
    } else {
      throw invalidProviderError(b);
    }
  });
  return res;
}

2.建立 Token 时为了不命名冲突,尽可能避免使用字符串做为Token。

3.若要建立模块内通用的依赖对象,须要在 NgModule 中注册相关的 provider,若在每一个组件中,都有惟一的依赖对象,就须要在 Component 中注册相关的 provider。

4.multi providers 的具体做用,具体请参考 - Angular2 Multi Providers

5.Provider 是用来描述与 Token 关联的依赖对象的建立方式。当咱们使用 Token 向 DI 系统获取与之相关连的依赖对象时,DI 会根据已设置的建立方式,自动的建立依赖对象并返回给使用者。

Provider接口

export interface ClassProvider {
  // 用于设置与依赖对象关联的Token值,Token值多是Type、InjectionToken、OpaqueToken的实例或字符串
  provide: any; 
  useClass: Type<any>;
  // 用于标识是否multiple providers,如果multiple类型,则返回与Token关联的依赖对象列表
  multi?: boolean; 
}
  
export interface ValueProvider {
  provide: any;
  useValue: any;
  multi?: boolean;
}
  
export interface ExistingProvider {
  provide: any;
  useExisting: any;
  multi?: boolean;
}
  
export interface FactoryProvider {
  provide: any;
  useFactory: Function;
  deps?: any[]; // 用于设置工厂函数的依赖对象
  multi?: boolean;
}

总结

在文章的最后,想举一个现实生活中的例子,帮助初学者更好地理解 Angular 2 DI 和 Provider。

Provider 中的 token 能够理解为菜名,useClass、useValue能够理解为菜的烹饪方式,而依赖对象就是咱们所点的菜,而 DI 系统就是咱们的厨师了。若是没有厨师,咱们就得关心煮这道菜须要哪些原材料,怎么煮菜,重要的是还得本身煮,可想而知多麻烦。而有了厨师(DI),咱们只要在菜谱上点菜,必要时备注一下烹饪方式,不过多久香喷喷的菜就上桌鸟~~~。

相关文章
相关标签/搜索