概述
服务是一个普通的JS对象,以一种应用其它部分可以直接使用的方式来提供通用的逻辑代码。所以在应用*享的代码应该放在服务当中。这里作者对于服务也进行了分类,依据它们的角色
- Injectable -- 提供一个特性,和Angular的DI协作使得其注入到组件中
- Non-injectable -- JS对象,不使用Angular的DI系统,而是直接引入到文件中(例如应用的main文件)
- Helper -- 让组件的使用更容易
- Data -- 应用间的共享数据(用户登录信息)
依赖注入(Dependency Injection)和注入树(Injector Trees)
服务经常依赖于DI系统。DI通过一个injector(将依赖注入的实体)和一个provider(知道如何构造实例化对象的实体)来工作。一个模块有顶层的injector来负责模块内所有的实体。每个组件都有自己的injector。当你在组件的元数据中声明一个providers
数组,这个组件会得到自己的injector,对于没有声明的组件,它会共享父组件的injector(依次往上共享),injector会在injector树中往上找,知道找到它需要的注入实体(或者抛出异常)
injector创建了层次结构,他也让我们能单独在应用的某一部分进行注入或者覆盖更高等级的注入器(injector)。当项目规模扩大,不同团队协作的过程中,同名服务不可避免,如果都定义在顶层,很容易造成冲突,那么将服务以更低等级(靠近使用它们的组件)进行声明,可以最大程度避免冲突
在上图的上半部分,由App模块的provider来提供服务实例,所以每个组件获取的Post服务都是同一个实例(由同一个provider提供)。下半部分中,Forum和Blog组件都提供了注入Post服务,所以它们不会再往注入树上层查找服务,因此它们将获得服务的不同实例
我们也可以将provider重新映射到树中的其它节点,通过providers: [{ provide: PostsService, useClass: PostsServiceV2 }]
的方法,可以用同样的名字使用不同的服务。覆盖服务的方式由以下几种
在注册一个provider的时候,对象的名字被当作token,并且用它来查找值。当你将服务的引用放到providers
数组,DI将这个服务名当作token来查找服务。当我们注入服务并且给它类型值,DI会根据名字来查找服务,然后返回对应的值,如果我们覆盖了默认的provider的值,DI会返回覆盖后的值。不适用JS的模块加载而使用DI的一些原因
- 专注于使用服务而不是创建服务
- 帮你解决依赖问题
- 易于测试,总是可以模拟一个假服务
- 在不担心其它地方也使用了这个服务的前提下控制服务的注入
- 每个注入树中的服务实例是干净的
这里是作者对于DI和服务使用提供的一些建议
- Inject at the lowest level -- 将服务添加到最低层级组件的providers中,即只使用这个服务的组件
- Name your service wisely -- 服务需要一个有意义的名字
- Keep services focused -- 不要将所有功能放到一个服务中,服务规模越大越难以维护和测试
- Keep services meaningful -- 在服务的数量和角色上保持平衡
- Use consistent patterns -- 设计服务要具有一致性
不需要DI的服务
有一些情况下,你不需要使用DI或者你需要一个服务在Angular没有完全准备好时可用(例如配置服务)。方法就是使用JS的模块来在文件之间导入或导出值
Http拦截器
作者建议当你需要在超过一个类型的请求或响应上应用逻辑或者在一般服务的基础上添加新的功能,可用使用拦截器
Helper Services
重用代码,你可以创建一个服务将这些帮助函数暴露出来简化组件的工作。通过将复杂性从组件级别转移到服务级别,可用使组件的功能更加专注
用来共享的服务
组件之间传递数据主要依靠input和事件绑定。另一个选项是通过服务和依赖注入。你要做的是让服务把数据抛出去,在任何需要使用数据的方法把服务以注入的方式引进来使用。如果你通过数据绑定的方式在组件中使用,那么change detection会自动捕获(服务的)改变然后渲染
当应用越来越复杂,保证你的数据以你要求的方式暴露出来是很重要的
其它的服务
在查看Angular文档时,API被分为不同的类别,包括Pipe、Class、Function和Decorator。这些列出来的东西都是Angular本身所导出的,所以可以在任何地方引用,有些甚至可以被注入(Location是一个类,但是很像一个提供了向浏览器URL读取/写入值的服务)(Date Pipe可以在模板外对日期进行格式化)