每個組件都有一個被Angular管理的生命周期,Angular提供了生命周期鉤子,把這些關(guān)鍵生命時刻暴露出來,賦予我們在它們發(fā)生時采取行動的能力。
| 鉤子 | 目的和時機(jī) |
|---|---|
| ngOnChanges() | 當(dāng)Angular(重新)設(shè)置數(shù)據(jù)綁定輸入屬性時響應(yīng)。 該方法接受當(dāng)前和上一屬性值的SimpleChanges對象當(dāng)被綁定的輸入屬性的值發(fā)生變化時調(diào)用,首次調(diào)用一定會發(fā)生在ngOnInit()之前。 |
| ngOnInit() | 在Angular第一次顯示數(shù)據(jù)綁定和設(shè)置指令/組件的輸入屬性之后,初始化指令/組件。在第一輪ngOnChanges()完成之后調(diào)用,只調(diào)用一次。 |
| ngDoCheck() | 檢測,并在發(fā)生Angular無法或不愿意自己檢測的變化時作出反應(yīng)。在每個Angular變更檢測周期中調(diào)用,ngOnChanges()和ngOnInit()之后. |
| ngAfterContentInit() | 當(dāng)把內(nèi)容投影進(jìn)組件之后調(diào)用。第一次ngDoCheck()之后調(diào)用,只調(diào)用一次。只適用于組件。 |
| ngAfterContentChecked() | 每次完成被投影組件內(nèi)容的變更檢測之后調(diào)用,ngAfterContentInit()和每次ngDoCheck()之后調(diào)用只適合組件。 |
| ngAfterViewInit() | 每次做完組件視圖和子視圖的變更檢測之后調(diào)用。ngAfterViewInit()和每次ngAfterContentChecked()之后調(diào)用。只適合組件。 |
| ngAfterViewChecked() | 每次做完組件視圖和子視圖的變更檢測之后調(diào)用。ngAfterViewInit()和每次ngAfterContentChecked()之后調(diào)用。只適合組件。 |
| ngOnDestroy() | 當(dāng)Angular每次銷毀指令/組件之前調(diào)用并清掃。 在這兒反訂閱可觀察對象和分離事件處理器,以防內(nèi)存泄漏。在Angular銷毀指令/組件之前調(diào)用。 |

下面我們來看代碼
// 子組件
import {AfterContentChecked,AfterContentInit,AfterViewChecked,AfterViewInit,
DoCheck, OnChanges, OnDestroy, OnInit,SimpleChange} from '@angular/core';
import { Component, Input } from '@angular/core';
import { LoggerService } from './logger.service';
let nextId = 1;
export class PeekABoo implements OnInit {
constructor(private logger: LoggerService) { }
// 在組件首次加載的時候執(zhí)行,在第一次ngOnchange后
ngOnInit() { this.logIt(`OnInit`); }
logIt(msg: string) {
this.logger.log(`#${nextId++} ${msg}`);
}
}
@Component({
selector: 'peek-a-boo',
template: '<p>Now you see my hero, {{name}}</p>',
styles: ['p {background: LightYellow; padding: 8px}']
})
export class PeekABooComponent extends PeekABoo implements
OnChanges, OnInit, DoCheck,
AfterContentInit, AfterContentChecked,
AfterViewInit, AfterViewChecked,
OnDestroy {
@Input() name: string;
private verb = 'initialized';
constructor(logger: LoggerService) {
super(logger);
let is = this.name ? 'is' : 'is not';
this.logIt(`name ${is} known at construction`);
}
// 當(dāng)父組件的變量變化時,自組建的屬性也會改變.
ngOnChanges(changes: SimpleChanges) {
let changesMsgs: string[] = [];
for (let propName in changes) {
if (propName === 'name') {
let name = changes['name'].currentValue;
changesMsgs.push(`name ${this.verb} to "${name}"`);
} else {
changesMsgs.push(propName + ' ' + this.verb);
}
}
this.logIt(`OnChanges: ${changesMsgs.join('; ')}`);
this.verb = 'changed'; // next time it will be a change
}
// 在每次需要做出改變的時候的觸發(fā)
ngDoCheck() { this.logIt(`DoCheck`); }
// 組件內(nèi)容初始化,只在組件周期發(fā)生一次
ngAfterContentInit() { this.logIt(`AfterContentInit`); }
// 在ngoncheck之后發(fā)生
ngAfterContentChecked() { this.logIt(`AfterContentChecked`); }
ngAfterViewInit() { this.logIt(`AfterViewInit`); }
ngAfterViewChecked() { this.logIt(`AfterViewChecked`); }
// 組件銷毀
ngOnDestroy() { this.logIt(`OnDestroy`); }
}
service代碼是輸出打印信息的
import { Injectable } from '@angular/core';
@Injectable()
export class LoggerService {
logs: string[] = [];
prevMsg = '';
prevMsgCount = 1;
log(msg: string) {
if (msg === this.prevMsg) {
// 若之前的msg和新的msg相同時,count加1.
this.logs[this.logs.length - 1] = msg + ` (${this.prevMsgCount += 1}x)`;
} else {
// New message; log it.
this.prevMsg = msg;
this.prevMsgCount = 1;
this.logs.push(msg);
}
}
clear() { this.logs.length = 0; }
tick() { this.tick_then(() => { }); }
tick_then(fn: () => any) { setTimeout(fn, 0); }
}
我們很奇怪的子組件是如何來使用services的,來看父組件
// 父組件
import { Component } from '@angular/core';
import { LoggerService } from './logger.service';
@Component({
selector: 'peek-a-boo-parent',
template: `
<div class="parent">
<h2>Peek-A-Boo</h2>
// 控制子組件的顯影
<button (click)="toggleChild()">
{{hasChild ? 'Destroy' : 'Create'}} PeekABooComponent
</button>
<button (click)="updateHero()" [hidden]="!hasChild">Update Hero</button>
<peek-a-boo *ngIf="hasChild" [name]="heroName">
</peek-a-boo>
<h4>-- Lifecycle Hook Log --</h4>
<div *ngFor="let msg of hookLog">{{msg}}</div>
</div>
`,
styles: ['.parent {background: moccasin}'],
providers: [ LoggerService ] //父組件的service可供子組件使用
})
export class PeekABooParentComponent {
hasChild = false;
hookLog: string[];
heroName = 'Windstorm';
private logger: LoggerService;
constructor(logger: LoggerService) {
this.logger = logger;
this.hookLog = logger.logs;
}
toggleChild() {
this.hasChild = !this.hasChild;
if (this.hasChild) {
this.heroName = 'Windstorm';
this.logger.clear(); // clear log on create
}
this.logger.tick();
}
updateHero() {
this.heroName += '!';
this.logger.tick();
}
}
下面我們來看一下好玩的東西。。。
import { Directive, OnInit, OnDestroy } from '@angular/core';
import { LoggerService } from './logger.service';
let nextId = 1;
// 在這里定義了一個指令,上面有2個鉤子函數(shù)
@Directive({selector: '[mySpy]'})
export class SpyDirective implements OnInit, OnDestroy {
constructor(private logger: LoggerService) { }
ngOnInit() { this.logIt(`onInit`); }
ngOnDestroy() { this.logIt(`onDestroy`); }
private logIt(msg: string) {
this.logger.log(`Spy #${nextId++} ${msg}`);
}
}
究竟上面的代碼有何用意?
// spy.component.html
<div class="parent">
<h2>Spy Directive</h2>
<input [(ngModel)]="newName" (keyup.enter)="addHero()">
<button (click)="addHero()">Add Hero</button>
<button (click)="reset()">Reset Heroes</button>
<p></p>
//這一步很關(guān)鍵,這些個div具有了生命周期鉤子,從myspy獲得
<div *ngFor="let hero of heroes" mySpy class="heroes">
{{hero}}
</div>
<h4>-- Spy Lifecycle Hook Log --</h4>
<div *ngFor="let msg of spyLog">{{msg}}</div>
</div>
@Component({
selector: 'spy-parent',
templateUrl: './spy.component.html',
styles: [
'.parent {background: khaki;}',
'.heroes {background: LightYellow; padding: 0 8px}'
],
providers: [LoggerService]
})
export class SpyParentComponent {
newName = 'Herbie';
heroes: string[] = ['Windstorm', 'Magneta'];
spyLog: string[];
constructor(private logger: LoggerService) {
this.spyLog = logger.logs;
}
addHero() {
if (this.newName.trim()) {
this.heroes.push(this.newName.trim());
this.newName = '';
this.logger.tick();
}
}
removeHero(hero: string) {
this.heroes.splice(this.heroes.indexOf(hero), 1);
this.logger.tick();
}
reset() {
this.logger.log('-- reset --');
this.heroes.length = 0;
this.logger.tick();
}
}
點(diǎn)擊add時,添加一個英雄,會打印ngoninit
我們接著來看一個很好解釋ngonchange的例子。
// parent.html
<div class="parent">
<h2>{{title}}</h2>
<table>
<tr><td>Power: </td><td><input [(ngModel)]="power"></td></tr>
<tr><td>Hero.name: </td><td><input [(ngModel)]="hero.name"></td></tr>
</table>
<p><button (click)="reset()">Reset Log</button></p>
<on-changes [hero]="hero.name" [power]="power"></on-changes>
</div>
下面這個代碼很長,仔細(xì)看
import {
Component, Input, OnChanges,
SimpleChanges, ViewChild
} from '@angular/core';
class Hero {
constructor(public name: string) {}
}
@Component({
selector: 'on-changes',
template: `
<div class="hero">
<p>{{hero}} can {{power}}</p>
<h4>-- Change Log --</h4>
<div *ngFor="let chg of changeLog">{{chg}}</div>
</div>
`,
styles: [
'.hero {background: LightYellow; padding: 8px; margin-top: 8px}',
'p {background: Yellow; padding: 8px; margin-top: 8px}'
]
})
export class OnChangesComponent implements OnChanges {
@Input() hero: Hero;
@Input() power: string;
changeLog: string[] = [];
ngOnChanges(changes: SimpleChanges) {
for (let propName in changes) {
let chng = changes[propName];
let cur = JSON.stringify(chng.currentValue);
let prev = JSON.stringify(chng.previousValue);
this.changeLog.push(`${propName}: currentValue = ${cur}, previousValue = ${prev}`);
}
}
reset() { this.changeLog.length = 0; }
}
/***************************************/
@Component({
selector: 'on-changes-parent',
templateUrl: './on-changes-parent.component.html',
styles: ['.parent {background: Lavender;}']
})
export class OnChangesParentComponent {
hero: Hero;
power: string;
title = 'OnChanges';
@ViewChild(OnChangesComponent) childView: OnChangesComponent;
constructor() {
this.reset();
}
reset() {
// new Hero object every time; triggers onChanges
this.hero = new Hero('Windstorm');
// setting power only triggers onChanges if this value is different
this.power = 'sing';
if (this.childView) { this.childView.reset(); }
}
}
有關(guān)viewChild在以前的章節(jié)已經(jīng)講過,可以回看之前的文章。需要注意的是這種吧2個組件寫在一起的形式。
上面講述了3個生命周期鉤子函數(shù)調(diào)用的例子,還需自己去體味才行。