资讯专栏INFORMATION COLUMN

angular 服务随记

smartlion / 3614人阅读

摘要:告诉注入器如何创建该服务,可以通过设置元数据来配置注入器种方式在服务本身的装饰器中。装饰器具有一个名叫的元数据选项,在这里指定把被装饰类的放到注入器中,或某个特定的注入器中。

angular 服务随记 依赖注入

创建服务需要用到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;
}

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/104103.html

相关文章

  • JavaScript·摘抄·随记(持续补充中)

    摘要:无主题,内容为感触较深的一些答疑探讨等,摘自多篇文章,侵删为什么是单线程的单线程,与它的用途有关。作为浏览器脚本语言,的主要用途是与用户互动,以及操作。这决定了它只能是单线程,否则会带来很复杂的同步问题。摘自运行机制详解再谈作者阮一峰 无主题,内容为感触较深的一些答疑、探讨等,摘自多篇文章,侵删 1、为什么JavaScript是单线程 JavaScript的单线程,与它的用途有关。作...

    young.li 评论0 收藏0
  • 《高性能javascript》随记 - Loading and Excecution

    摘要:此过程中,页面的解析与用户的交互都是阻塞的。非阻塞脚本延时脚本可以给标签添加一个属性,这个属性表明元素中的脚本不打算修改,因此代码可以稍后执行。此技术的重点在于无论在何处启动下载,脚本的下载和运行都不会阻塞页面的处理过程。 当浏览器遇到标签时,页面的加载、介些都会停下来,运行此javascript代码,然后再继续加载。这种事情同样会发生在那些以src属性调用的外部脚本,浏览器首先下载外...

    DevTTL 评论0 收藏0
  • 《高性能javascript》随记 - DOM Scripting

    摘要:操作的代价非常昂贵,对元素节点的访问和修改样式布局的改变以及事件的绑定都影响着网页的性能。所以,尽量减少对布局信息的查询次数,并用局部变量参与计算。当动画结束时,重新定位,从而只一次下移文档其他元素的位置。这样可以最小化事件句柄数量。 DOM操作的代价非常昂贵,对元素节点的访问和修改、样式、布局的改变以及DOM事件的绑定都影响着网页的性能。 批量修改DOM 如果要对元素节...

    Ryan_Li 评论0 收藏0

发表评论

0条评论

smartlion

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<