angular 服务随记小结


Posted in Javascript onMay 06, 2019

依赖注入

创建服务需要用到Injectable,@Injectable() 装饰器把类标记为可供注入的服务,不过在使用该服务的 provider 配置好 Angular 的依赖注入器之前,Angular 实际上无法将其注入到任何位置。

provider告诉注入器如何创建该服务,可以通过设置元数据来配置注入器(3种方式):

  • 在服务本身的 @Injectable() 装饰器中。
  • 在 NgModule 的 @NgModule() 装饰器中。
  • 在组件的 @Component() 装饰器中。

@Injectable() 装饰器具有一个名叫 providedIn 的元数据选项,在这里指定把被装饰类的provider放到 root 注入器中,或某个特定 NgModule 的注入器中。

@NgModule() 和 @Component() 装饰器都有用一个 providers 元数据选项,在那里你可以配置 NgModule 级或组件级的注入器。

注入器与服务实例

在某个注入器范围内,服务是单例的。应用只有一个根注入器,angular具有多级注入器系统,以为者下级注入器可以创建自己的服务实例。

每当 Angular 创建一个在 @Component() 中指定了 providers 的组件实例时,它也会为该实例创建一个新的子注入器。 类似的,当在运行期间加载一个新的 NgModule 时(即lazy module),Angular 也可以为它创建一个拥有自己的提供商的注入器。

借助注入器继承机制,仍然可以把全应用级的服务注入到这些组件中。 组件的注入器是其父组件注入器的子节点,也是其父节点的父节点的后代,以此类推,直到应用的根注入器为止。 Angular 可以注入该继承谱系中任何一个注入器提供的服务。

模块化编程时,service、component、pipe等最好都放在module中,需要引入这些服务时,通过导入module来引用,不要直接import service 和component,这不符合模块化思想。

多级注入系统

应用程序中有一个与组件树平行的注入器树,对于在什么级别上注入会最终导致:

  • 最终包的大小
  • 服务的范围
  • 服务的生命周期

当在服务自身的@Injectable()装饰器中指定provider时,CLI生产模式所用的优化工具可以进行摇树优化,它会移除那些没有用过的服务,摇树优化生成的包更小。

三级provider

  • root级,是AppModule全局的,配置方法已提。
  • NgModule级,两种方法:可以在module的@NgModule 的 provider 元数据中指定;也可以在@injectable() 的providerIn选项中指定某个模块类

如果模块是lazy modole,需要使用@NgModule的provider选项。

  • 组件级为每个component实例配置自己的注入器

无论对于根级注入器还是模块级注入器,服务实例的生存期都和应用或模块本身相同。Angular 可以把这个服务实例注入到任何需要它的类中(即app内是单例的)。Angular 只能把相应的服务注入到该组件实例或其下级组件实例中,而不能把这个服务实例注入到其它地方(即组件内并不是单例的)。

注入器冒泡

当一个组件申请获得一个依赖时,Angular 先尝试用该组件自己的注入器来满足它。 如果该组件的注入器没有找到对应的提供商,它就把这个申请转给它父组件的注入器来处理。 如果那个注入器也无法满足这个申请,它就继续转给它在注入器树中的父注入器。 这个申请继续往上冒泡 —— 直到 Angular 找到一个能处理此申请的注入器或者超出了组件树中的祖先位置为止。 如果超出了组件树中的祖先还未找到,Angular 就会抛出一个错误。

单例服务

在angular中创建单例服务有两种方式:

  • 在创建服务时声明该服务在应用的根上提供
  • 把该服务包含在AppModule或者某个只会被AppModule导入的模块中

这里第一条很容易理解。重点第二条:当通过@NgMododule()来声明一个serivce时,这个服务在AppModule内将会是单例的,当一个module中提供了一个service,当另一个lazy module导入了这个模块时,angular会为它创一个子注入器,会重新创建service的实例,此service也就多了一个实例。

forRoot()

如果某个模块同时提供了服务提供商和可声明对象(组件、指令、管道),那么当在某个子注入器中加载它的时候(比如lazy module),就会生成多个该服务提供商的实例。 而存在多个实例会导致一些问题,因为这些实例会屏蔽掉根注入器中该服务提供商的实例,而它的本意可能是作为单例对象使用的。 因此,Angular 提供了一种方式来把服务提供商从该模块中分离出来,以便该模块既可以带着 providers 被根模块导入,也可以不带 providers 被子模块导入。

如上文所述,当在运行期间加载一个新的 NgModule 时(即lazy module),Angular 也可以为它创建一个注入器,所以此时导入的其他模块中的service就生成了多个实例,而forRoot可以保证并不创建新的service实例,而是去引用root注入器中的service实例,也就保证了service依然是个单例服务。

Code

在懒加载模块中导入有service的TestDIModule模块

@NgModule({
 imports: [
 CommonModule,
 BatteryRoutingModule,
 TestDIModule
 ],
 declarations: [BatteryWidgetComponent, BatteryTwoComponent,
 DemoComponent]
})

在TestDIModule模块中

@NgModule({
 imports: [
 CommonModule
 ],
 declarations: [TestDiComponent],
 exports: [TestDiComponent],
 providers: [ ]
})
export class TestDIModule {
 static forRoot(): ModuleWithProviders {
 return {
  ngModule: TestDIModule,
  providers: [
  TestDiService
  ]
 };
 }
 }

在根模块中引入TestDIModule模块

imports: [
 BrowserModule,
 TestDIModule.forRoot(),
 ],

最后在根模块路由中添加这个懒加载模块

const routes: Routes = [
 { path: 'battery', loadChildren: './battery-widget/battery.widget.module#BatteryWidgetModule' },
];

@NgModule({
 exports: [ RouterModule ],
 imports: [ RouterModule.forRoot(routes)
 ],
})

作为测试,可以在TestDIModule中的service中打log看一下

import { Injectable, ModuleWithProviders } from '@angular/core';
import { TestDIModule } from './test-di.module'

@Injectable()
export class TestDiService {

 constructor() {
 console.log('->TestDiService');
 }

 addCoount() {
 this.count++;
 console.log('->count', this.count);
 }

 count = 0;
}

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
IE6/IE7中JavaScript json提示缺少标识符、字符串或数字问题处理
Dec 16 Javascript
jquery实现增加删除行的方法
Feb 03 Javascript
原生js结合html5制作简易的双色子游戏
Mar 30 Javascript
Bootstrap每天必学之js插件
Nov 30 Javascript
ReactJs快速入门教程(精华版)
Nov 28 Javascript
基于JS实现的随机数字抽签实例
Dec 08 Javascript
.net MVC+Bootstrap下使用localResizeIMG上传图片
Apr 21 Javascript
CSS3结合jQuery实现动画效果及回调函数的实例
Dec 27 jQuery
详解Vue Elememt-UI构建管理后台
Feb 27 Javascript
vue中slot(插槽)的介绍与使用
Nov 12 Javascript
JS/HTML5游戏常用算法之碰撞检测 包围盒检测算法详解【矩形情况】
Dec 13 Javascript
Vue-CLI 项目在pycharm中配置方法
Aug 30 Javascript
详解如何使用nvm管理Node.js多版本
May 06 #Javascript
关于AOP在JS中的实现与应用详解
May 06 #Javascript
JS使用iView的Dropdown实现一个右键菜单
May 06 #Javascript
一文读懂ES7中的javascript修饰器
May 06 #Javascript
JavaScript中AOP的实现与应用
May 06 #Javascript
使用 vue 实现灭霸打响指英雄消失的效果附demo
May 06 #Javascript
vue如何截取字符串
May 06 #Javascript
You might like
Php中文件下载功能实现超详细流程分析
2012/06/13 PHP
关于尾递归的使用详解
2013/05/02 PHP
如何使用PHP获取指定日期所在月的开始日期与结束日期
2013/08/01 PHP
PHP文件生成的图片无法使用CDN缓存的解决方法
2015/06/20 PHP
PHP版本升级到7.x后wordpress的一些修改及wordpress技巧
2015/12/25 PHP
限制复选框的最大可选数
2006/07/01 Javascript
在Javascript中为String对象添加trim,ltrim,rtrim方法
2006/09/22 Javascript
js关闭父窗口时关闭子窗口
2013/04/01 Javascript
javacript使用break内层跳出外层循环分析
2015/01/12 Javascript
JavaScript正则表达式的分组匹配详解
2016/02/13 Javascript
使用vue.js制作分页组件
2016/06/27 Javascript
JS简单实现tab切换效果的多窗口显示功能
2016/09/07 Javascript
BootStrap Validator 版本差异问题导致的submitHandler失效问题的解决方法
2016/12/01 Javascript
微信小程序商城项目之淘宝分类入口(2)
2017/04/17 Javascript
jQuery实现一个简单的验证码功能
2017/06/26 jQuery
JavaScript内存泄漏的处理方式
2017/11/20 Javascript
谈谈JS中的!!
2017/12/07 Javascript
使用socket.io制做简易WEB聊天室
2018/01/02 Javascript
vue2.0 自定义 饼状图 (Echarts)组件的方法
2018/03/02 Javascript
浅谈vue首屏加载优化
2018/06/28 Javascript
webpack 从指定入口文件中提取公共文件的方法
2018/11/13 Javascript
图片文字识别(OCR)插件Ocrad.js教程
2018/11/26 Javascript
vscode配置vue下的es6规范自动格式化详解
2019/03/20 Javascript
微信小程序自定义组件传值 页面和组件相互传数据操作示例
2019/05/05 Javascript
Vue.js项目实战之多语种网站的功能实现(租车)
2019/08/07 Javascript
js实现盒子移动动画效果
2020/08/09 Javascript
[54:43]DOTA2-DPC中国联赛 正赛 CDEC vs Dynasty BO3 第一场 2月22日
2021/03/11 DOTA
3分钟学会一个Python小技巧
2018/11/23 Python
解决python测试opencv时imread导致的错误问题
2019/01/26 Python
python语言元素知识点详解
2019/05/15 Python
利用Django模版生成树状结构实例代码
2019/05/19 Python
北美三大旅游网站之一:Travelocity
2017/08/12 全球购物
香港艺人陈冠希创办的潮流品牌:JUICESTORE
2021/03/04 全球购物
C#里面如何判断一个Object是否是某种类型(如Boolean)?
2016/02/10 面试题
应届大学生求职的自我评价
2013/11/17 职场文书
党委书记个人检查对照材料思想汇报
2014/10/11 职场文书