iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 15
1
Modern Web

Angular 全集中筆記系列 第 15

第 15 型 - Angular 生命週期 (Lifecycle) - onChanges / onInit / doCheck / onDestroy

  • 分享至 

  • xImage
  •  

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) 時,建議一併實作所對應的介面,以降低方法名稱拼錯的機會。

ngOnChanges

需要注意的是,如果在 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

從上圖結果可見,在待辦事項新增了一次資料後,並沒有觸發 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) 方法的觸發時間點。

OnInit

自訂的變更檢測

如上面實例中提到,因在新增待辦事項時,不會改變事項清單屬性的參考,而不觸發 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 規則中,不建議同時實作 ngChangesDoCheck,故上面實作程式會出現 TSLint 的警告訊息。

doCheck

元件實體銷毀通知

當離開應用程式頁面時,Angular 會銷毀該頁面內的元件與指令實體,而在銷毀之前會觸發 ngOnDestroy() 鉤子 (Hook) 方法。實務上,會在此方法中釋放不會自動被垃圾回收的資料,以避免記憶體洩漏的風險;這些資源大致包含了:

  • 針對 DOM 事件或可監控物件的訂閱取消
  • interfvale 計時器的停止
  • 取消註冊此指令所註冊過的回呼 (Callback) 方法

另外,也可以在此方法中,用來通知應用程式的其他部份此元件將消失,使其他元件進行相對應作業。

結論

這一篇先針對生命週期 (Lifecycle) 中,初始化、屬性值變更監控以及銷毀等鉤子 (Hook) 方法進行說明,而在下一篇會說明投影內容變更的相關元素與事件。


上一篇
第 14 型 - 管道 (Pipe) - DatePipe
下一篇
第 16 型 - Angular 生命週期 (Lifecycle) - AfterContentInit / AfterContentChecked
系列文
Angular 全集中筆記30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言