简介
Angular 指令的生命周期,它是用来记录指令从创建、应用及销毁的过程。Angular 提供了一系列与指令生命周期相关的钩子,便于我们监控指令生命周期的变化,并执行相关的操作。Angular 中所有的钩子如下图所示:
生命周期介绍
- 指令与组件共有的钩子
- ngOnChanges
- ngOnInit
- ngDoCheck
- ngOnDestroy
- 组件特有的钩子
- ngAfterContentInit
- ngAfterContentChecked
- ngAfterViewInit
- ngAfterViewChecked
调用顺序
ngOnChanges - 当数据绑定输入属性的值发生变化时调用 ngOnInit - 在第一次 ngOnChanges 后调用 ngDoCheck - 自定义的方法,用于检测和处理值的改变 ngAfterContentInit - 在组件内容初始化之后调用 ngAfterContentChecked - 组件每次检查内容时调用 ngAfterViewInit - 组件相应的视图初始化之后调用 ngAfterViewChecked - 组件每次检查视图时调用 ngOnDestroy - 指令销毁前调用
ngOnChanges
- 用处:当Angular设置数据绑定输入属性发生变化时响应。
- 时机:当被绑定的输入属性的值发生变化时调用,不过首次调用是会发生在ngOnInit()之前的。 ngOnChanges()方法获取了一个对象,它可以同时观测1个/多个绑定的属性值,它把每个发生变化的属性名都映射到了一个SimpleChange对象, 该对象中有属性的当前值和前一个值。
- 触发条件 @input属性(输入属性)发生变化时,会调用。非此属性,不会调用。 当输入属性为对象时,当对象的属性值发生变化时,不会调用,当对象的引用变化时会触发。 输入属性,使用@Input装饰的属性,这里还需要注意不可变对象,在Angular中,典型的不可变对象是string类型,但所有自定义对象均为可变对象,如:user:{name:string},可变对象即使被定义为输入属性,也不会触发OnChanges方法。
export interface SimpleChanges { [propName: string]: SimpleChange; }
SimpleChange的结构如下:
export declare class SimpleChange {
previousValue: any;
currentValue: any;
firstChange: boolean;
constructor(previousValue: any, currentValue: any, firstChange: boolean);
/**
* Check whether the new value is the first value assigned.
*/
isFirstChange(): boolean;
}
假如我们的组件中有一个满足触发OnChanges钩子条件的属性名叫inputVal,那么可以通过如下方式获取它变化前后的值:
// Angular定义SimpleChanges类构造函数三个参数分别为上一个值,当前值和是否第一次变化(firstChange: boolean),这些changes都可以调用。
ngOnChanges(changes: SimpleChanges): void {
console.log('ngOnChanges中inputVal变更前值为:'+ changes['inputVal'].previousValue);
console.log('ngOnChanges中inputVal变更后值为:'+ changes['inputVal'].currentValue);
console.log('ngOnChanges中inputVal是否是一次改变:'+ changes['inputVal'].firstChange);
}
栗子
====================补充分割线====================
关于在组件中监听某属性值的变化问题,在angularjs1.x中,可以使用$watch
进行实时监听,但在ng2后取消了$watch指令,以至于当遇到所需场景是有一种手足无措的感觉。
在刚遇到该问题的时候,我采用了ngDoCheck()
,多次的检查以及无数的无法预估问题让我放弃,进而调研ngOnChange()
,发现貌似必须要在@Input
的前提下使用(未确定,欢迎指正),不符合场景要求,依然无法满足需求。
随后在【segmentfault】提出问题,收获两个思路,个人比较推荐第二个思路,符合ng4的设计模式,故而分享出来:
- 思路 1:
// 在service中声明该变量,并利用定时器不断调用
@Injectable()
class CustomService{
public customAttr: number = 0;
constructor() {
setInterval(() => this.customAttr = Math.random(), 1000);
}
}
constructor(public customService: CustomService) { }
<div>{{customService.customAttr}}</div>
- 思路 2 :
// 引入 BehaviorSubject 模块,也可以直接使用Subject订阅模式,BehaviorSubject 更优在于每次只获取最后一次的值
import { BehaviorSubject } from 'rxjs/BehaviorSubject'
...
export CustomService {
customAttr: BehaviorSubject<any[]> = new BehaviorSubject<[]>
...
}
// 每次修改下拉值只需(设注入service为CustomService ):
// CustomService .downData.next(更新的数据)
// 所有的子组件引用服务后只需在模版使用async pipe便可自动监听订阅数据变化:
<div *ngFor="let item of (CustomService .downData | async)"></div>
// 也可以直接在组件中订阅:
CustomService .downData.subscribe(data => {
// 监听到变化
...
})