zoukankan      html  css  js  c++  java
  • angular11源码探索六[服务基础一]

    服务

    Angular依赖项注入现在是Angular的核心部分,并允许将依赖项注入到组件或类中

    依赖注入(DI)是一种技术,在这种技术中,我们将一个对象的实例提供给另一个依赖于它的对象。这种技术也称为“控制反转”(IoC)

    IoC — 控制反转

    DI — 依赖注入

    IOC

    三个原则

    • 高层次的模块不应该依赖于低层次的模块,它们都应该依赖于抽象
    • 抽象不应该依赖于具体实现,具体实现应该依赖于抽象
    • 面向接口编程 而不要面向实现编程

    IOC,是一种设计原则:

    通过将组件(Components)的设置和使用分开,来降低类别或模组之间的耦合度(即,解耦)

    控制反转,指实例依赖成员的[控制流程],由主动控制变成被动控制,因此被称为控制反转

    案例:

    • 打开iPhone,打开微信,进入群聊,抢红包
    • 打开iPhone,打开短信,编辑短信内容,选择收件人,发送短信
    • 打开iPhone,打开小米商城,选择需要抢购的商品,等待到达抢购时间,开始抢购

    这些过程和iPhone耦合的很紧密,如果小明需要换手机了,那么那需要在每个过程中都将iPhone换为新手机。这时,就需要控制反转,我们不再主动去获得手机,而是被动的接收一个手机。

    接收手机

    • 打开手机,打开微信,进入群聊,抢红包
    • 打开手机,打开短信,编辑短信内容,选择收件人,发送短信
    • 打开手机,打开小米商城,选择需要抢购的商品,等待到达抢购时间,开始抢购

    我们发现,过程和具体的一个手机(例如,iPhone)耦合度降低了,可以很简单的更改手机的实例。

    另一个例子

    我们去网咖打游戏,是不可能自己带游戏去网咖的,而是网咖都把这些游戏提供好了

    需要的游戏,不用自己下载,而是网咖提供给你。

    ==>

    需要的依赖实例,不用主动(Active)建立,而是被动(Passive)接收。

    简单来说,A依赖B,但A无法控制B的创建和销毁,仅使用B,那么B的控制权交给C(A以外)处理

    • 第一个例子,小明是A,依赖手机B,C可以是其他任何给小明手机的人(可能有点牵强)
    • 第二个例子,我们是A,依赖游戏B,C则是网吧

    依赖注入DI

    刚才那两个例子中,小明需要手机,我们需要玩游戏,那么有哪些方法可以让我们被动接收这些东西呢?假设小明是A,手机是B

    • 通过A的接口,把B传入
    • 通过A的构造,把B传入
    • 通过设置A的属性,把B传入

    这个过程就是依赖注入,即A啥都没干,就可以直接使用B,比如我们啥也不用干,就可以直接玩游戏,真香。

    也可以说,依赖注入是实现控制反转的一种方式,它们之间有着密切的联系

    案例一

    class Id {
      static getId(type: string): Id {
        return new Id()
      }
    }
    
    class Address {
      constructor(city, street) { }
    }
    
    class Person {
      id: Id
      address: Address
      constructor(id: Id, address: Address) {
        this.id = id
        this.address = address
      }
    }
    
    // 在某个类当中调用的时候
    main() {
      const id = Id.getId('123')
      const address = new Address('北京', '北京')
      const person = new Person(id, address)
    }
    

    我们也将依赖提升到了入口处的 main() 当中,但是在当下这种形式中,我们已经知道如果有新的需求变动,我们还是需要去模块的内部来进行修改,下面我们就来看看如何在 Angular 当中来解决这个问题的

    Angular 的依赖注入中主要有三个概念

    • Injector,注入者,利用其提供的 API 去创建依赖的实例
    • Provider,告诉 Injector 如何去创建一个实例(构造这个对象)
    • Object,创建好的对象,也就是当前所处的模块或者组件需要的依赖(某种类型的对象,依赖本身也是一种类型)

    小案例demo

    服务
    @Injectable()
    export class StateNewService {
      constructor() {}
      getLog() {
        return '1111'
      }
    }
    
    模块 注入
    @NgModule({
     
      providers:[StateNewService]
    })
    页面使用
    export class TwoComponent implements OnInit {
      constructor(private states:StateNewService) {
        console.log(states.getLog());
      }
    }    
    

    类型令牌

    StateNewService 用作搜索查找的令牌,然后我们新类StateNewService ,组件可以使用它

      providers:[{ provide: StateNewService, useClass: StateNewService }]
    其实也等价于下面这种写法
       [{
          provide:StateNewService,
          useFactory:()=>new StateNewService()
        }]
    

    替换当前class

    @Injectable()
    export class CowService  {
    
      makeNoise() {
        return 'mooo!';
      }
    
    }
    @Injectable()
    export class TestService {
    
      constructor() { }
      makeNoise() {
        return '333';
      }
    }
    
     providers: [
        {provide:TestService,useClass:CowService}
         ]
    
    使用的时候
    constructor(
        private other: TestService
      ) {
        console.log(other.makeNoise()); // mooo!
    

    字符串令牌

    依赖项是值或者对象或者类表示

    服务
    @Injectable()
    export class StateNewService {
      constructor() {}
      getLog() {
        return '1111'
      }
    }
    
    模块
    @NgModule({
      providers:[
        { provide: 'STATE_NEW_SERVICE', useClass: StateNewService },
        {provide:'APIURL', useValue: 'http://SomeEndPoint.com/api' },
        {provide:'USE_FAKE', useValue: true },
      ]
    })
    使用
    export class TwoComponent implements OnInit {
      constructor(
        @Inject('STATE_NEW_SERVICE') private states: StateNewService,
        @Inject('APIURL') private apiURL:string,
        @Inject('USE_FAKE') private use:boolean ) {
        console.log(states.getLog());
        // two.component.ts:26 1111
        console.log(apiURL);
        // two.component.ts:27 http://SomeEndPoint.com/api
        console.log(use);
        // true
      }
    }
    

    字符串令牌更易于错误键入,这使得在大型应用程序中难以跟踪和维护。

    Angular提供了InjectionToken类,以确保创建了唯一令牌。通过创建类的新实例来创建注入令牌InjectionToken

    InjectionToken

    • 创建 Token 的时候为了避免命名冲突,尽量避免使用字符串作为 Token
    • 若要创建模块内通用的依赖对象,需要在 NgModule 中注册相关的 provider
    • 若在每个组件中都有唯一的依赖对象,就需要在 Component 中注册相关的 provider

    首先,创建一个单独的文件,并将其命名为tokens.ts

    • InjectionToken@angular/core库导入。创建的实例InjectionToken并将其分配给constAPI_URL
    import { InjectionToken } from '@angular/core';
    export const API_URL= new InjectionToken<string>(''); 
    
    • 在module,令牌用于在providers元数据中注册值
    import { API_URL } from './tokens';
    
    providers: [ 
       { provide: API_URL, useValue: 'http://SomeEndPoint.com/api' }
    ] 
    
    • 直接使用

      import { API_URL } from './tokens';
       
      constructor(@Inject(API_URL) private apiURL: string) { 
      }
      

    你可以把multi:true 打印的时候之前是字符串现在你发现返回的是数组,原理就是相同的token 注册多个值

     providers:[
        {provide:API_URL, useValue: 'http://SomeEndPoint.com/api',multi:true },
      ]
    

    当我们给令牌添加第二个token

     providers:[
        {provide:API_URL, useValue: 'http://SomeEndPoint.com/api1',multi:true },
        {provide:API_URL, useValue: 'http://SomeEndPoint.com/api2',multi:true },
      ]
     constructor(
         @Inject(API_URL) private apiURL:string
      ) {
        console.log(apiURL);
         // ["http://SomeEndPoint.com/api1", "http://SomeEndPoint.com/api2"]
    

    我们发现如果移除其中一个multi:true ,我们会发现打印的时候报错

    如果发现如果两个都移除multi:true ,我们会发现打印的是api2,下面的会把上面的替换掉

  • 相关阅读:
    Fiddler抓包使用教程-会话图标
    Fiddler抓包使用教程-安装配置
    Fiddler抓包使用教程-扫盲篇
    Java web 开发填坑记 2 -如何正确的创建一个Java Web 项目
    Java Web 开发填坑记- 如何正确的下载 Eclipse
    Android 监听 WiFi 开关状态
    Android 获取系统语言(兼容7.0)
    Android 监听手机GPS打开状态
    Android Monkey的使用
    Android 开源库和项目 3
  • 原文地址:https://www.cnblogs.com/fangdongdemao/p/14152885.html
Copyright © 2011-2022 走看看