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 相关文章推荐
IE的有条件注释判定IE版本详解(附实例代码)
Jan 04 Javascript
javascript实现全局匹配并替换的方法
Apr 27 Javascript
js文本框走动跑马灯效果代码分享
Aug 25 Javascript
javascript基础语法——全面理解变量和标识符
Jun 02 Javascript
基于BootStrap与jQuery.validate实现表单提交校验功能
Dec 22 Javascript
Js实现中国公民身份证号码有效性验证实例代码
May 03 Javascript
使用Node.js实现ORM的一种思路详解(图文)
Oct 24 Javascript
js登录滑动验证的实现(不滑动无法登陆)
Jan 03 Javascript
js动态添加表格逐行添加、删除、遍历取值的实例代码
Jan 25 Javascript
Vue中使用Sortable的示例代码
Apr 07 Javascript
vue项目打包之后背景样式丢失的解决方案
Jan 17 Javascript
在JavaScript中查找字符串中最长单词的三种方法(推荐)
Jan 18 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函数之子字符串替换 str_replace
2011/03/23 PHP
对象失去焦点时自己动提交数据的实现代码
2012/11/06 PHP
对laravel in 查询的使用方法详解
2019/10/09 PHP
javascript 闭包疑问
2010/12/30 Javascript
浅说js变量
2011/05/25 Javascript
JS随机漂浮广告代码具体实例
2013/11/19 Javascript
jQuery多媒体插件jQuery Media Plugin使用详解
2014/12/19 Javascript
原生js实现图片层叠轮播切换效果
2016/02/02 Javascript
js实现异步循环实现代码
2016/02/16 Javascript
javascript ES6中箭头函数注意细节小结
2017/02/17 Javascript
原生js实现放大镜特效
2017/03/08 Javascript
使用canvas及js简单生成验证码方法
2017/04/02 Javascript
正则表达式基本语法及表单验证操作详解【基于JS】
2017/04/07 Javascript
微信小程序收藏功能的实现代码
2018/06/12 Javascript
浅谈angularJS2中的界面跳转方法
2018/08/31 Javascript
js找出5个数中最大的一个数和倒数第二大的数实现方法示例小结
2020/03/04 Javascript
JavaScript Array.flat()函数用法解析
2020/09/02 Javascript
Nuxt.js nuxt-link与router-link的区别说明
2020/11/06 Javascript
[10:34]DOTA2上海特级锦标赛全纪录
2016/03/25 DOTA
[09:40]DAC2018 4.5 SOLO赛 MidOne vs Miracle
2018/04/06 DOTA
Python ORM框架SQLAlchemy学习笔记之关系映射实例
2014/06/10 Python
python登录豆瓣并发帖的方法
2015/07/08 Python
Python 的类、继承和多态详解
2017/07/16 Python
python数据结构之链表详解
2017/09/12 Python
Python获取指定文件夹下的文件名的方法
2018/02/06 Python
python 字典操作提取key,value的方法
2019/06/26 Python
Python类反射机制使用实例解析
2019/12/30 Python
Django模型中字段属性choice使用说明
2020/03/30 Python
基于python 凸包问题的解决
2020/04/16 Python
在网络中有两台主机A和B,并通过路由器和其他交换设备连接起来,已经确认物理连接正确无误,怎么来测试这两台机器是否连通?如果不通,怎么来判断故障点?怎么排
2014/01/13 面试题
学前教育毕业生自荐信范文
2013/12/24 职场文书
企业党的群众路线教育实践活动领导班子对照检查材料
2014/09/25 职场文书
刑事辩护授权委托书格式
2014/10/13 职场文书
优秀员工推荐材料
2014/12/20 职场文书
研究生简历自我评
2015/03/11 职场文书
交通安全温馨提示语
2015/07/14 职场文书