Angular 應用程式在運行時,每個元件從實體化並渲染頁面,至離開頁面而銷毀元件實體,整個生命週期 (Lifecycle) 中 Angular 會檢查資料屬性的變化情況,並更新頁面與元件。在開發 Angular 應用程式時,可以利用 Angular 提供的生命週期鉤子方法 (Lifecycle hook methods),在特定觸發事件中處理應用程式所需的事項。這一篇將依生命週期 (Lifecycle) 順序說明每個鉤子方法 (Hook methods) 的用途。
如果先前文章實作中,在輸入屬性值有變化時會呼叫 ngOnChanges()
鉤子方法 (hook methods),並傳入 SimpleChanges
物件,此物件記錄了所變化的屬性上一個與當前值,以及是否為首次變更等資訊。除此之外,此方法也會在 ngOnInit()
被執行前觸發。
透過在 task-list.component.ts 中實作 OnChanges
介面,並加入 ngOnChanges
方法將 SimpleChanges
物件值顯示 Chorme DevTools 中。由此可觀察到在頁面載入後,即會有 tasks
屬性第一次的變化資訊;而在點選「載入資料」按鈕後,則觸發了第二次變化。
export class TaskListComponent implements OnChanges {
@Input() tasks: Task[];
ngOnChanges(changes: SimpleChanges): void {
console.log("TaskListComponent - ngOnChanges", changes);
}
}
在使用生命週期鉤子方法 (Lifecycle hook methods) 時,建議一併實作所對應的介面,以降低方法名稱拼錯的機會。
需要注意的是,如果在 AppComponent 中加入「新增資料」的按鈕,並在點選此按鈕時新增一筆待辦事項至清單中。
<div>
<span>
<button type="button" (click)="onLoad()">載入資料</button>
<button type="button" (click)="onClear()">清空資料</button>
<button type="button" (click)="onAdd()">新增資料</button>
</span>
<span>實際完成率:{{ completeRate | percent: '1.0-2' }}</span>
</div>
<app-task-list [tasks]="tasks"></app-task-list>
<pre>{{ tasks | json }}</pre>
export class AppComponent {
onAdd(): void {
const task = new Task("顯示多筆的待辦事項清單");
task.level = "S";
task.tags = ["feature"];
this.tasks.push(task);
}
}
從上圖結果可見,在待辦事項新增了一次資料後,並沒有觸發 ngOnChanges()
鉤子方法 (hook method)。這是因為只有在輸入屬性值變更時才會觸發此方法,而 tasks
屬性的值是對待辦事項陣件的參考 (reference),且新增事項至 tasks
陣列中並不會改變其參考,故不會觸發 ngOnChanges
方法。
接著在首次 ngOnChange()
方法的呼叫後會觸發 ngOnInit()
方法,而此方法在整個生命週期 (Lifecycle) 中只會被呼叫一次,實務上常會在此針對元件屬性值進行初始化作業。順帶一提,在開發 Angular 元件時,可以在類別的建構式 (constructor) 或 ngOnInit()
方法進行屬性值的初始化作業,不過元件建構式 (constructor) 應該是簡單且安全的,因此在建構式 (constructor) 中只會把屬性設定為簡單的值;如果需要從外部取得初始資料時,則會將在 ngOnInit()
進行實作與設定。
export class TaskListComponent implements OnInit, OnChanges {
@Input() tasks: Task[];
ngOnInit(): void {
console.log("TaskListComponent - ngOnInit");
}
ngOnChanges(changes: SimpleChanges): void {
console.log("TaskListComponent - ngOnChanges", changes);
}
}
透過在 task-list.component.ts 中實作 OnInit
介面,透過 Chorme DevTools 中可以觀察此鉤子 (Hook) 方法的觸發時間點。
如上面實例中提到,因在新增待辦事項時,不會改變事項清單屬性的參考,而不觸發 ngOnChanges
鉤子 (Hook) 方法。對於此種無法觸發 ngOnChanges()
鉤子方法 (hook method) 的變更,可以在 DoCheck()
方法中自行實作變更的檢查邏輯,此方法會在 ngOnInit()
之後以及每次執行 ngOnChanges()
方法之後被觸發。需要注意的是,由於在每個變更檢測週期都會觸發此方法,使得此方法會有很高的呼叫頻率,所以在此實作的檢查邏輯必須非常輕量,否則會降低使用者的體驗。
在 task-list.component.ts 中實作 DoCheck
介面,並加入 ngDoCheck
方法簡單地將待辦事項清單的筆數顯示在 Chorme DevTools 內。而可以觀察到此方法的觸發時機,且在新增待辦事項資料時,雖然不會觸發 ngOnChanges()
方法,但觸發了 ngDoCheck()
方法。
export class TaskListComponent implements OnInit, OnChanges, DoCheck {
@Input() tasks: Task[];
ngOnInit(): void {
console.log("TaskListComponent - ngOnInit");
}
ngOnChanges(changes: SimpleChanges): void {
console.log("TaskListComponent - ngOnChanges", changes);
}
ngDoCheck(): void {
console.log("TaskListComponent - ngDoCheck", this.tasks.length);
}
}
在預設的 tslint 規則中,不建議同時實作
ngChanges
與DoCheck
,故上面實作程式會出現 TSLint 的警告訊息。
當離開應用程式頁面時,Angular 會銷毀該頁面內的元件與指令實體,而在銷毀之前會觸發 ngOnDestroy()
鉤子 (Hook) 方法。實務上,會在此方法中釋放不會自動被垃圾回收的資料,以避免記憶體洩漏的風險;這些資源大致包含了:
另外,也可以在此方法中,用來通知應用程式的其他部份此元件將消失,使其他元件進行相對應作業。
這一篇先針對生命週期 (Lifecycle) 中,初始化、屬性值變更監控以及銷毀等鉤子 (Hook) 方法進行說明,而在下一篇會說明投影內容變更的相關元素與事件。