一、依赖注入
(一)依赖注入理解
- 先来看一段代码:
class Engine {
// Engine类的构造函数,需要接受一个number类型的参数
constructor(speed: number) {
}
}
class Car {
private engine: Engine; // 声明一个私有属性,类型为Engine
constructor() {
// 在构造函数中实例化Engine
this.engine = new Engine(200);
}
}
在这段代码中,Car
类里有一个私有属性engine
,是Engine
类的实例对象,此时这个属性的创建过程就会严重依赖Engine
类的定义,如果Engine
类需要接受的参数个数或类型发生改变,就需要立马修改engine
属性的创建代码,代码的耦合度过高。
- 依赖注入就是为了降低代码耦合度而提出来的概念。依赖注入(Dependency Injection),简称DI,类之间的依赖关系由容器来负责。看下面一段代码:
class Engine {
constructor(speed: number) {}
}
// 在类的外部实例化Engine类
const engine = new Engine(200);
class Car {
private engine: Engine;
constructor(engine: Engine) {
// 直接使用
this.engine = engine;
console.log(engine)
}
}
const car = new Car(engine)
在类的外部创建engine
,这样Car
类就不要管engine
的传参问题,就不需要与Engine
类耦合了。对象的创建过程Angular
内部已经帮我们实现好了,所以我们在使用服务时,不需要再次创建,只要在constructor
的参数列表中声明并且使用变量接收即可。
(二)Injector的创建和使用
- 概念
Angular有自己的DI框架帮我们实现类的实例化。我们只需要通过简单的代码就可以实现复杂的依赖注入。
Angular的DI框架主要有四个重要的概念:- Dependency 组件要依赖的实例对象,即服务的实例对象
- Token 获取服务实例对象的标识。Angular内部维护了很多的实例对象,在获取时需要通过Token来判断要获取哪一个实例对象
- Injector 注入器,负责维护并创建实例对象,并且要找到组件需要的实例对象,通过参数的方式传递给组件
- Provider 用来配置注入器的对象,指定创建服务实例对象的类,以及指定Token
- Injector 注入器
- 创建注入器
在app.moudule.ts根模块中引入ReflectiveInjector模块
虚拟一个服务类,创建一个注入器import {ReflectiveInjector} from '@angular/core';
class injectorService { constructor() {} run(){ console.log('run') } } // 创建注入器,接受一个数组,数组里放服务类 const injector = ReflectiveInjector.resolveAndCreate([injectorService])
- 获取服务实例,并且运行实例方法
控制台成功运行了实例的方法:// 向get中传递Token标识,Token就是这个类,返回值就是这个类的实例对象 const myInjector = injector.get(injectorService) myInjector.run()
3. 同一个注入器不会重复创建一个服务的实例,创建的同时就会缓存这个实例,因此服务是单例模式const myInjector1 = injector.get(injectorService) const myInjector2 = injector.get(injectorService) console.log(myInjector1 === myInjector2) // true
- 不同的注入器创建出来的实例不一样
// 创建子级注入器 const childInjector = injector.resolveAndCreateChild([injectorService]) const myInjector1 = injector.get(injectorService) const myInjector3 = childInjector.get(injectorService) console.log(myInjector1 === myInjector3) // false
- 注入器查找服务实例的方式类似于作用域链,当在当前注入器中找不到时,会向父级注入器查找,都找不到才会创建新的实例
// 创建注入器 const injector = ReflectiveInjector.resolveAndCreate([injectorService]) // 子级注入器不传服务 const childInjector = injector.resolveAndCreateChild([]) const myInjector1 = injector.get(injectorService) // 当前注入器中没有找到,会向上级注入器查找 const myInjector3 = childInjector.get(injectorService) console.log(myInjector1 === myInjector3) // true
- 创建注入器
- Provider
上面创建注入器的形式其实是一种简写方式,下面来看完整的写法
通过Provide指定获取实例对象的标识,也就是token。当provide和useClass相同的时候,就可以直接写类
useClass表明当前注入器存储的是一个类const injector = ReflectiveInjector.resolveAndCreate([{ provide: "myInjectorService", useClass: injectorService }]) const myInjector1 = injector.get("myInjectorService")
注入器不仅可以用来存储类,也可以用来存储对象,用注入器存储的对象往往是唯一的,不可变的const injector = ReflectiveInjector.resolveAndCreate([{ provide: "user", useValue: Object.freeze({ userName:'张三', userToken:'123456' }) }]) const user = injector.get('user')
(二)服务
- 服务的创建与引入
- 使用cli提供的命令行工具创建服务:
ng g s shared/user
,会在shared模块下创建一个服务
- 书写服务代码
import { Injectable } from '@angular/core'; @Injectable({ providedIn: 'root' }) export class UserService { constructor() { } getUser() { return { name: '张三', age: 20, address: '北京市' } } }
- 在组件类中引入服务
import {UserService} from "../../user.service";
public user = this.userService.getUser(); ngOnInit() { console.log(this.user) }
- 使用cli提供的命令行工具创建服务: