Angular为什么需要service
- 组件应该是专注于展示层,所以需要service来获取数据和保存数据。
- 组件之间的通信需要service来协助完成。
众所周知,angular中service采用的是依赖注入,那什么是依赖注入呢?
为什么需要依赖注入
从一个小例子说起
export class Car {
public engine: Engine; // 引擎
public tires: Tires; // 轮胎
constructor() {
this.engine = new Engine();
this.tires = new Tires();
}
}
//如果更换了引擎的构造函数,要传入气缸数,构造函数则要跟着改
constructor() {
this.engine = new Engine(12);
this.tires = new Tires();
}
上面创建了一个汽车类,但是这个类完全没有健壮性。假如引擎的构造函数改变了,构建引擎的时候需要传一个参数,我们就得去汽车类里面改变new Engine()这个使用。如果仅仅是需要改两个地方可能还能接受,但是如果引擎类还依赖别的零件,这么一层层的依赖关系,其中一个构造函数要发生改变了,可能会导致一连串的改变,会导致写出来的代码完全没有可维护性。轮胎也是如此,不同的汽车需要不同的轮胎,那么像上述这么写就不太灵活,没有通用性可言。
所以这样就需要依赖注入
export class Car {
constructor(public engine: Engine, public tires: Tires) { }
}
let car = new Car(new Engine(), new Tires());
这样 engine和tire 和car类就是分离的,只要你是传入的引擎和轮胎是满足要求的,那么这个car就是okay的。
class Engine2 {
constructor(public cylinders: number) { }
}
// 如果需要一个12缸的引擎, 这样我们完全不用动car类。
let bigCylinders = 12;
let car = new Car(new Engine2(bigCylinders), new Tires());
依赖注入这种模式也为编写测试用例提供了方便,在测试car类的时候,你不必去关系轮胎细节,只要给出一个可用的轮胎就行。
上面解释了依赖注入是什么,以及为什么需要依赖注入。就是一个类从外界接受他所需要的依赖关系,而不是自己去创造它们。但是对于消费者,问题又来了。想要一辆车,就必须得有一个工厂来组装出一个车
import { Engine, Tires, Car } from './car';
export class CarFactory {
createCar() {
let car = new Car(this.createEngine(), this.createTires());
car.description = 'Factory';
return car;
}
createEngine() {
return new Engine();
}
createTires() {
return new Tires();
}
}
这个模式看来起似乎没有问题,但是这只是简化的依赖,类似的引擎工厂类, 轮胎工厂类,相互调用,相互依赖,这会形成一个不可维护的🕸️
这个时候就需要一个依赖注入框架了。这个框架有一个叫注入器的东西,需要一个Car时, 只需要
let car = injector.get(Car);
这样消费者也不用去维护生产car的工厂类。并且car类本身也不管轮胎,引擎怎么来的。皆大欢喜。
angular中依赖注入模式
angular中采用的是分层依赖注入
angular的应用程序是一个组件树,每一个组件实例化都有自己的注入器providers。
组件在找service的时候会一直向上冒泡寻找,直到找到根组件AppComponent还没有找到的时候就会报错。
这样设计的好处:
- 便于组件之间的通信。顶层一点的组件可以统一控制多个子组件的对同一个数据的操作。
- 增加复用。可以抽出多个底层组件复用的service写在上层组件中。
angular中service的小知识
- 生成一个module的service。 ng g service test --module=app
- 采用 @Injectable()装饰器,这个东西告诉angular这个服务类可能本身注入了依赖,
- 组件本身是不管服务内部怎么运作。
- service是必须通过providers注入。
- 是可以在服务的构造函数中调用一些业务函数 ,但不是最佳实践。
- 服务在app内是单例的。
src/app/test.service.ts
import { Injectable } from '@angular/core';
@Injectable()
export class TestService {
constructor() { }
}