Angular25 組件的生命周期鉤子
1 生命周期鉤子概述
組件共有9個生命周期鉤子
1.1 生命周期的執行順序
技巧01:測試時父組件傳遞對子組件的輸入屬性進行初始化操作
import { Component, Input, SimpleChanges, OnInit, OnChanges, DoCheck, AfterContentChecked, AfterViewInit, AfterContentInit, AfterViewChecked, OnDestroy } from ‘@angular/core‘; let logIndex : number = 1; @Component({ selector:TS‘life‘, templateUrl: ‘./life.component.html‘, styleUrls: [‘./life.component.scss‘] }) export class LifeComponent implements OnInit, OnChanges, DoCheck, AfterContentInit, AfterContentChecked, AfterViewInit, AfterViewChecked, OnDestroy { @Input() name : string; logIt(msg : String) { console.log(`#${logIndex++} - ${msg}`); } constructor() { this.logIt("name屬性在constructor裏面的值為: " + this.name); } ngOnInit() { this.logIt("ngOnInit"); } ngOnChanges(changes : SimpleChanges){ this.logIt("name屬性在ngOnChanges裏面的值為: " + this.name); } ngDoCheck() { this.logIt("ngDoCheck"); } ngAfterContentInit() {this.logIt("ngAfterContentInit"); } ngAfterContentChecked() { this.logIt("ngAfterContentChecked"); } ngAfterViewInit() { this.logIt("ngAfterViewInit"); } ngAfterViewChecked() { this.logIt("ngAfterViewChecked"); } ngOnDestroy() { this.logIt("ngOnDestroy"); } }
2 ngOnChanges
2.1 可變對象和不可變對象
在JavaScript中string類型是不可變對象,自定義的對象是可變對象;例如:
var a = "hello"; -> 變量a中存儲的是字符串“hello”對應的內存地址
a = "warrior"; -> 變量a中存儲的是字符串“warrior”對應的內存地址
結論:將不同的字符串賦值給同一個變量時,變量的值會發生改變;所以string類型是不可變類型【PS: 跟Java一樣】
var user : {name : string} = {name : "warrior"}; -> 變量user中存儲的是對象{name : "warrior"}對應的內存地址
user.name = "fury"; -> 變量user中還是存儲的對象{name : "fury”}對應的內存地址,而且{name : "warrior"}和{name : "fury”}是同一個對象,所以他們的內存地址相同,故變量user中存儲的值沒有發生改變,改變的僅僅是變量user所指向的那個對象中的內容而已
結論:修改一個對象的內容並不會改變這個對象的內存地址,所以對象是可變對象
2.2 ngOnChanges
當輸入屬性的值發生改變時就會觸發ngOnChanges
技巧01:如果輸入屬性的類型是一個對象時,需要區分是可變對象還是不可變對象,只有不可變對象時才會觸發ngOnChanges;總之記住只要輸入屬性的值發生改變才會觸發ngOnChanges
技巧02:ngOnChanges方法需要傳入一個 SimpleChanges 類型的參數,可以利用該參數來查看輸入屬性改變前後的值,以及是否是初次賦值
技巧03:JSON.stringfy() 方法在將數據轉化成JSON時可以進行格式化,例如
ngOnChanges(simpleChanges : SimpleChanges) {
console.log(JSON.stringify(simpleChanges, null, 2));
}
2.2.1 子組件代碼
<div class="panel panel-primary"> <div class="panel-heading">子組件</div> <div class="panel-body"> <p>問候語:{{greeting}}</p> <p>姓名:{{user.name}}</p> <p>年齡:{{age}}</p> <p> 消息:<input type="text" name="message" [(ngModel)]="message" /> </p> </div> <div class="panel-footer">{{currentDate | date : "yyyy-MM-dd HH:mm:ss"}}</div> </div>HTML
import { Component, OnInit, EventEmitter, Output, Input, OnChanges, SimpleChanges } from ‘@angular/core‘;
@Component({
selector: ‘child‘,
templateUrl: ‘./child.component.html‘,
styleUrls: [‘./child.component.scss‘]
})
export class ChildComponent implements OnInit, OnChanges {
@Input()
greeting : string;
@Input()
user : {name : string};
@Input()
age : number;
message : string;
currentDate : Date;
constructor() { }
ngOnInit() {
this.currentDate = new Date();
setInterval(() => {
this.currentDate = new Date();
}, 1000);
}
ngOnChanges(simpleChanges : SimpleChanges) {
console.log(JSON.stringify(simpleChanges, null, 2));
}
}
TS
2.2.2 父組件代碼
<div class="panel panel-primary"> <div class="panel-heading">父組件</div> <div class="panel-body"> <p> 問候語:<input type="text" name="greeting" [(ngModel)]="greeting" /> </p> <p> 姓名:<input type="text" name="name" [(ngModel)]="user.name" /> </p> <p> 年齡:<input type="number" [(ngModel)]="age" /> </p> <child [greeting]="greeting" [user]="user" [age]="age"></child> </div> <div class="panel-footer">{{currentDate | date : "yyyy-MM-dd HH:mm:ss"}}</div> </div>HTML
import { Component, OnInit } from ‘@angular/core‘;
@Component({
selector: ‘parent‘,
templateUrl: ‘./parent.component.html‘,
styleUrls: [‘./parent.component.scss‘]
})
export class ParentComponent implements OnInit {
greeting : string = "Hello";
user : {name : string} = {name : "王楊帥"};
age : number = 8;
currentDate : Date;
constructor() { }
ngOnInit() {
this.currentDate = new Date();
setInterval(() => {
this.currentDate = new Date();
}, 1000);
}
}
TS
2.2.3 效果圖
3 ngDoCheck
3.1 變更檢測機制
angular中由zone.js去負責監聽瀏覽器中所有異步事件(事件[單擊、雙擊]、定時器、ajax)來保證模板和組件屬性的變化時同步的;
只要zone.js檢測到有異步事件發生時所有監測機制是默認檢測機制的組件都會觸發ngDoCheck
應用:當輸入屬性的類型是可變對象時,即使可變對象的內容發生了變化 ngOnchanges 也不會被調用,但是 ngDoCheck 會被調用;所以我們可以利用 ngDoCheck 來檢測可變對象的變化
坑01:zone.js檢測到任何一個組件有異步事件發生都會讓所有采用默認變更檢測機制的組件執行 ngDoCheck,所以 ngDoCheck 方法要慎用,而且邏輯不能太復雜,不然會影響性能
3.2 變更檢測策略
3.2.1 default
angular默認的變更檢測策略
如果所有的組件都是默認的變更檢測策略,那麽當一個組件發生改變時angular會對所有的組件進行變更檢查
3.2.2 onPush
待更新...
3.3 代碼匯總
3.3.1 父組件代碼
<div class="panel panel-primary"> <div class="panel-heading">父組件</div> <div class="panel-body"> <p> 問候語:<input type="text" name="greeting" [(ngModel)]="greeting" /> </p> <p> 姓名:<input type="text" name="name" [(ngModel)]="user.name" /> </p> <p> 年齡:<input type="number" [(ngModel)]="age" /> </p> <child [greeting]="greeting" [user]="user" [age]="age"></child> </div> <div class="panel-footer">{{currentDate | date : "yyyy-MM-dd HH:mm:ss"}}</div> </div>HTML
import { Component, OnInit } from ‘@angular/core‘;
@Component({
selector: ‘parent‘,
templateUrl: ‘./parent.component.html‘,
styleUrls: [‘./parent.component.scss‘]
})
export class ParentComponent implements OnInit {
greeting : string = "Hello";
user : {name : string} = {name : "王楊帥"};
age : number = 8;
currentDate : Date;
constructor() { }
ngOnInit() {
this.currentDate = new Date();
// setInterval(() => {
// this.currentDate = new Date();
// }, 1000);
}
}
TS
3.3.2 子組件代碼
<div class="panel panel-primary"> <div class="panel-heading">子組件</div> <div class="panel-body"> <p>問候語:{{greeting}}</p> <p>姓名:{{user.name}}</p> <p>年齡:{{age}}</p> <p> 消息:<input type="text" name="message" [(ngModel)]="message" /> </p> </div> <div class="panel-footer">{{currentDate | date : "yyyy-MM-dd HH:mm:ss"}}</div> </div>HTML
import { Component, OnInit, EventEmitter, Output, Input, OnChanges, SimpleChanges } from ‘@angular/core‘;
import { DoCheck } from ‘@angular/core/src/metadata/lifecycle_hooks‘;
@Component({
selector: ‘child‘,
templateUrl: ‘./child.component.html‘,
styleUrls: [‘./child.component.scss‘]
})
export class ChildComponent implements OnInit, OnChanges, DoCheck {
@Input()
greeting : string;
@Input()
user : {name : string};
oldName : string; // 存儲上一次的值
changeNum : number = 0; // 非本組件觸發ngDoCheck方法的次數
changeDetected : boolean; // 是否是本組件觸發ngDoCheck方法
@Input()
age : number;
message : string;
currentDate : Date;
constructor() { }
ngOnInit() {
this.currentDate = new Date();
// setInterval(() => {
// this.currentDate = new Date();
// }, 1000);
}
ngOnChanges(simpleChanges : SimpleChanges) {
console.log(JSON.stringify(simpleChanges, null, 2));
}
ngDoCheck() : void {
// 如果檢測到是本組件的輸入屬性變化
if (this.user.name !== this.oldName) {
this.changeDetected = true;
console.log("ngDoCheck: user.name 從 "+ this.oldName +" 變成了 "+ this.user.name +" ");
this.oldName = this.user.name;
}
if (this.changeDetected) {
this.changeNum = 0; // 本組件觸發的ngDoCheck就進行清零操作
} else { // 非本組件觸發的ngDoCheck方法就進行加一操作
this.changeNum = this.changeNum + 1;
console.log("ngDoCheck: user.name沒有變化,ngDoCheck方法被調用了" + this.changeNum + "次");
}
this.changeDetected = false;
}
}
TS
3.3.3 效果展示
Angular25 組件的生命周期鉤子