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 相关文章推荐
基于Jquery的动态添加控件并取值的实现代码
Sep 24 Javascript
解读JavaScript代码 var ie = !-[1,] 最短的IE判定代码
May 28 Javascript
千分位数字格式化(用逗号隔开 代码已做了修改 支持0-9位逗号隔开)的JS代码
Dec 05 Javascript
js实现Select下拉框具有输入功能的方法
Feb 06 Javascript
JS实现的车标图片提示效果代码
Oct 10 Javascript
js+canvas绘制五角星的方法
Jan 28 Javascript
Jquery基础之事件操作详解
Jun 14 Javascript
深入理解Node.js的HTTP模块
Oct 12 Javascript
微信小程序 本地存储及登录页面处理实例详解
Jan 11 Javascript
vue中倒计时组件的实例代码
Jul 06 Javascript
在JS循环中使用async/await的方法
Oct 12 Javascript
再也不怕 JavaScript 报错了,怎么看怎么处理都在这儿
Dec 09 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的知识
2006/11/17 PHP
PHP 计算代码执行耗时的代码修正网上普遍错误
2011/05/14 PHP
php中执行系统命令的方法
2015/03/21 PHP
详解PHP序列化反序列化的方法
2015/10/27 PHP
js 代码优化点滴记录
2012/02/19 Javascript
js 输出内容到新窗口具体实现代码
2013/05/31 Javascript
JavaScript中访问节点对象的方法有哪些如何使用
2013/09/24 Javascript
JavaScript按位运算符的应用简析
2014/02/04 Javascript
js实现鼠标滑过文字链接色彩变化的效果
2015/05/06 Javascript
Bootstrap每天必学之媒体对象
2015/11/30 Javascript
简单谈谈JS数组中的indexOf方法
2016/10/13 Javascript
深入理解js 中async 函数的含义和用法
2018/05/13 Javascript
Angular如何在应用初始化时运行代码详解
2018/06/11 Javascript
React如何解决fetch跨域请求时session失效问题
2018/11/02 Javascript
详解React中合并单元格的正确写法
2019/01/08 Javascript
Koa 中的错误处理解析
2019/04/09 Javascript
Node.js 实现抢票小工具 & 短信通知提醒功能
2019/10/22 Javascript
vue中改变滚动条样式的方法
2020/03/03 Javascript
JS模拟实现京东快递单号查询
2020/11/30 Javascript
在Python中使用全局日志时需要注意的问题
2015/05/06 Python
Python 日期区间处理 (本周本月上周上月...)
2019/08/08 Python
python__name__原理及用法详解
2019/11/02 Python
Python字符串、列表、元组、字典、集合的补充实例详解
2019/12/20 Python
基于css3仿造window7的开始菜单
2010/06/17 HTML / CSS
斯德哥尔摩通票:Stockholm Pass
2018/01/09 全球购物
考博自荐信
2013/10/25 职场文书
上班迟到检讨书
2014/01/10 职场文书
活动邀请函范文
2014/01/19 职场文书
药店主任岗位责任制
2014/02/10 职场文书
给学校的建议书范文
2014/05/15 职场文书
2014领导班子“四风问题”对照检查材料思想汇报(执法局)
2014/09/21 职场文书
幼儿教师师德师风自我评价
2015/03/05 职场文书
2016开学第一课心得体会
2016/01/23 职场文书
2016年党员创先争优承诺书
2016/03/25 职场文书
PHP新手指南
2021/04/01 PHP
python 通过使用Yolact训练数据集
2021/04/06 Python