understand angular life cycle hooks - ltoddy/blog GitHub Wiki
在Angular的应用中,从创建到销毁,组件的整个生命周期都由Angular管理.它使我们能够访问生命周期钩子函数,从而让我们 在组件的生命周期的关键时刻采取行动.
为了使用这些钩子函数,我们必须告诉Angular我们想要实现所需的钩子函数接口.Angular检查组件类并调用钩子函数(如果定义了的话), 例如(OnInit):
export class MyComponent implements OnInit {
constructor() {}
ngOnInit() {
// 业务逻辑
}
}
这是Angular提供的钩子函数列表,这些钩子函数以这种确切的顺序被调用.
lifecycle hooks |
---|
ngOnChanges() |
ngOnInit() |
ngDoCheck() |
ngAfterContentInit() |
ngAfterContentChecked() |
ngAfterViewInit() |
ngAfterViewChecked() |
ngOnDestroy() |
每次创建组件时都会调用此方法,然后当输入属性(@Input
)被改变时,此方法也都会被调用.
它接受一个SimpleChanges对象作为参数,该对象包含有关输入属性更改的信息:之前的值和现在的值.
export class MyComponent implements OnChanges {
ngOnChanges(changes: SimpleChanges) {
// 业务逻辑
}
}
如果你需要根据接收到的输入属性处理组件中的任何特定逻辑,这个钩子函数是非常有用的.
假设有一个显示用户信息的组件,这个组件接受UserInfo
对象为输入参数.下面是一个有关如何在该组件中
使用ngOnChanges
来添加逻辑以处理UserInfo
的例子:
export class UserInfoComponent implements OnChanges {
@Input() userInfo: UserInfo;
ngOnChanges(changes: SimpleChanges) {
const prevValue = changes['userInfo'].previouseValue;
const currValue = changes['userInfo'].currentValue;
// 业务逻辑
}
}
这个钩子函数只会在整个生命周期中调用一次,在第一次调用ngOnChanges
之后调用.
此时,在这个钩子函数中,您不仅可以访问数据绑定属性,还可以访问组件的输入属性.
export class MyComponent implements OnInit {
ngOnInit() {
// 业务逻辑
}
}
这个钩子函数在Angular的生命周期钩子中最常用的钩子函数之一.在这里,你可以调用异步操作,比如发送请求,也可以为要由
该组件处理的表单创建FormGroup
,设置订阅等等.基本上,在这里,你可以在构造组件后不久执行任何初始化.
假设你有一个注册表单的组件,并且想根据从后端服务请求的数据创建表单.以下是关于如何使用ngOnInit
实现
此目的的例子:
export class RegisterFormComponent implements OnInit {
formGroup: FormGroup;
constructor(private backendService: BackendService) {}
ngOnInit() {
this.backendService.getFormData()
.subscribe(response => {
this.formGroup = this.createForm(response.data);
});
}
private createForm(formFields: Array<FormField>): FormGroup {
// 创建FormGroup逻辑
}
}
或许,你可能想知道为什么将初始化的逻辑放在ngOnInit
中而不是构造器中,好吧,构造函数最好是留给依赖注入,而不添加任何业务逻辑,
我们的初始化逻辑就应该放在ngOnInit
上.
那是因为,构造函数是由JavaScript引擎处理,而不是Angular.这也是创建ngOnInit
的原因之一,该钩子函数被Angular调用,
并成为由它管理的组件生命周期的一部分.另外,当然,还不能在构造函数上访问输入属性.
这个钩子函数可以被视为ngOnChanges
的扩展.可以使用此方法来检测Angular无法或者不会检测到的更改.在ngOnChanges
和ngOnInit
调用之后,每次更改检测都会调用它.
export class MyComponent implements DoCheck {
curValue;
prevValue;
changeDetected: boolean = false;
ngDoCheck() {
if (this.prevValue !== this.currValue) {
this.changeDetected = true;
// 业务逻辑
}
}
}
这个钩子函数是非常的昂贵的,因为它被频繁的调用,在每个变更检测周期之后,无论变更发生在那里,都会调用.因此,应该小心的使用此钩子函数, 避免影响用户的体验.
在讨论这个之前,首先需要了解它们与什么相关.
AfterContent
钩子与ContentChild
有关,ContentChild
是Angular映射到组件中的子组件.
AfterView
钩子是与ViewChild
有关,ViewChild
是其显示在组件模板中的子组件.
假设我们下面有同时包含ContentChild
和ViewChild
的组件.我们有一个ng-content
标签,它将呈现从父级传递来的内容,
以及对ViewChildren
的引用(称为包装器).
@Component({
selector: 'app-my-component',
template: `
<div #wrapper>
<ng-content></ng-content>
</div>
`
})
export class MyComponent {
@ViewChild('wrapper') wrapper: ElementRef;
@ContentChild('content') content: ElementRef;
}
在ngDoCheck
第一次调用之后,这个钩子函数在组件的生命周期中仅被调用一次.在这个钩子函数中,在组件创建之后,
我们将第一次访问ContentChild
的ElementRef
;在Angular将外部内容映射到组件视图之后.
@Component({
selector: 'app-my-component',
template: `
<div>
<ng-content></ng-content>
</div>
`
})
export class MyComponent implements AfterContentInit {
@ContentChild('content') content: ElementRef;
ngAfterContentInit() {
// 现在可以访问: this.content
// 业务逻辑
}
}
在组件的生命周期里,这个方法在ngAfterContentInit
之后被调用一次,然后在随后的每个ngDoCheck
之后被调用,
@Component({
selector: 'app-my-component',
template: `
<div>
<ng-content></ng-content>
</div>
`
})
export class MyComponent implements AfterContentChecked {
@ContentChild('content') content: ElementRef;
ngAfterContentChecked() {
// 我们可以访问this.content,内容已被检查过了
// 业务逻辑
}
}
在组件生命周期里,这个钩子函数仅在ngAfterContentChecked
调用之后调用一次.在此方法中,可以首次访问ViewChild
的ElementRef
.
@Component({
selector: 'app-my-component',
template: `
<div #wrapper >
...
</div>
`
})
export class MyComponent implements AfterViewInit {
@ViewChild('wrapper') wrapper: ElementRef;
ngAfterViewInit() {
// 现在,可以访问this.wrapper
// 业务逻辑
}
}
当需要根据视图的组件加载视图上的内容时,此钩子函数是非常有用的.例如,当需要设置视频播放或者通过canvas元素创建图标时.
下面是有关如何使用ngAfterViewInit
钩子设置图标的例子:
@Component({
selector: 'app-my-component',
template: `
<div>
<canvas id="myCanvas" ></canvas>
</div>
`
})
export class MyComponent implements AfterViewInit {
ngAfterViewInit() {
// 现在可以通过id来获得canvas元素来创建图表
this.chart = new Chart('radarCanvas', {
...
});
}
}
在ngAfterViewInit
之后调用此方法一次,然后在后续的ngAfterContentChecked
之后调用此方法.
@Component({
selector: 'app-my-component',
template: `
<div #wrapper >
...
</div>
`
})
export class MyComponent implements AfterViewChecked {
@ViewChild('wrapper') wrapper: ElementRef;
ngAfterViewChecked() {
// 这里我们可以访问this.wrapper,视图已经被检查过了
// 业务逻辑
}
}
最后,在Angular销毁它之前,该钩子函数在组件的生命周期仅被调用一次. 在这个钩子函数中,应该放置该组件的所有清理逻辑,比如在这删除任何本地存储信息,取消订阅等,以避免内存泄漏.
export class MyComponent implements OnDestroy {
private mySubject: Subject<string> = new Subject();
ngOnDestroy() {
localStorage.removeItem('storageKey');
this.mySubject.unsubscribe();
}
}
用户刷新页面或者关闭浏览器的时候,不会调用ngOnDestroy
,因此,如果还需要在这些情况下处理一些清理逻辑.
则可以使用HostListener
装饰器:
@HostListener('window:beforeunload')
ngOnDestroy() {
// 清理逻辑
}
在创建Angular应用程序时,了解Angular生命周期,它们的目标以及何时调用它们将非常有用. 因此,重要的是要了解它们的工作原理以及可以从中获得什么,以便能够在需要时用它.