[NGX]Angular組件/指令生命週期簡介

Angular中全部的組件和指令都有相同的生命週期鉤子,在@angular/core庫中定義;可能不少初學者(包括我-。-)都只知道ngOnInit()和ngOnDestroy(),這裏簡單介紹一下Angular的幾個生命週期的順序和做用。html

執行順序

按照調用順序,Angular的生命週期鉤子有以下幾個,ajax

ngOnChanges() -> ngOnInit() -> ngDoCheck() -> ngAfterContentInit() -> ngAfterContentChecked() -> ngAfterViewInit() -> ngAfterViewChecked() -> ngOnDestroy()typescript

做用

ngOnChanges()

這個鉤子的第一次調用確定會在ngOnInit()執行前觸發,通常是用來檢測組件/指令的輸入屬性發生的變化用的,一旦該組件的輸入屬性(@Input)發生變化,ngx就會出發這個組件的ngOnChanges()方法;數組

用法:瀏覽器

ngOnChanges(changes: SimpleChanges) {
        console.log(changes);
        // 第一次調用ngOnchanges()時,changes.prop.firstChange === true;
        // 且changes.prop.previousValue === undefined
    }
複製代碼

注意:app

ngOnChanges()只能檢測到輸入屬性的變化,檢測不到內部屬性的變化,也就是說這個鉤子不像angular.js裏的$watch方法,$watch()方法是能夠監視內部屬性的變化並執行相應的回調函數的,而ngOnChanges()不能夠;函數

即便是針對輸入屬性,若是輸入屬性是字符串,數字等不可變類型,ngOnChanges()會在輸入屬性發生變化時觸發;oop

若是輸入屬性是數組,對象等引用類型,往數組裏push等,直接修改object.key的值是不會觸發ngOnChanges()的,Angular只關心屬性的引用是否發生變化,而不會關注對象的某個屬性是否發生變化。ui

若是你要在數組push時觸發ngOnChanges(),則應該將push改寫成this.array = [...this.array, item]this

修改對象key改寫爲this.obj = Object.assign({}, this.obj, {name: 'blabla'})或者this.obj = {...this.obj, name: 'blabla'}

這裏主要使用了es7的展開符作了一個簡單的示例,除此以外還能夠經過immutable.js等不可變數據類型實現。

ngOnInit()

Angular在對一個組件第一次顯示數據綁定和設置組件/指令的輸入屬性以後,初始化指令/組件。若是你用ng g component建立組件的話,@angular/cli建立的組件文件中是會自帶ngOnInit()的。

也就是說,ngOnInit()的執行是在組件/指令類的構造函數執行以後纔會執行的,它只會執行一次。一般狀況下,咱們會把一些初始化邏輯放進ngOnInit()裏面,如初始界面的數據的獲取等。因爲已經執行了構造函數,因此此時angular已經完成了輸入屬性的綁定。所以你能夠放心的使用這些屬性做爲參數發送ajax請求。

ngOnDestroy()

Angular每次銷燬組件/指令前會調用這個方法,一般在ngOnDestroy()裏咱們會對一些可觀察對象進行取消訂閱,對定時器進行取消等,防止內存泄漏;

有關取消訂閱,事實上不是全部的可觀察對象都須要你去手動取消訂閱,詳見https://medium.com/@benlesh/rxjs-dont-unsubscribe-6753ed4fda87

ngDoCheck()

上面咱們提到了ngOnChanges()裏面沒法對數組的push,對象的直接修改屬性值等操做感知變化,當時咱們採用了Object.assign和es7的展開符解決了這個問題。Angular自己提供了ngDoCheck()這個生命週期鉤子來偵測這些他沒法檢測到的變動。

這個鉤子相似angular.js中的$watch()方法,只要這個組件的屬性發生變化,就會觸發這個生命週期鉤子。所以它的觸發會很頻繁,因此不要在裏面寫很複雜的邏輯,這樣會致使程序的開銷異常的大。

ngAfterContentInit()和ngAfterContentChecked()

這兩個afterContent鉤子都是在Angular進行內容投影時觸發的。

ngAfterContentInit()第一次執行ngDoCheck()後執行,且只會執行一次。

內容投影就是將其餘組件的html內容插入到本組件指定位置的方法。一般組件中會有一個<ng-content></ng-content>元素做爲placeholder。

當組件的標籤中有其餘html標籤時,說明存在內容投影。

ngAfterContentChecked()會在每次完成被投影組件的變動檢測後執行,第一次執行是在第一次調用ngAfterContentInit()後執行。

這兩個鉤子發生在組件視圖組合完成以前,因此此時仍然是能夠更新組件屬性而不用擔憂觸發ExpressionChangedAfterItHasBeenCheckedError的。

ngAfterViewInit()和ngAfterViewChecked()

Angular會在每次建立了組件的子視圖後調用他們(也就是組件中的子組件的視圖);

要想訪問子視圖,須要經過@ViewChild()裝飾的屬性來實現。

// app.component.ts
import { Component, AfterViewInit, AfterViewChecked, ViewChild } from '@angular/core';
import { DetailComponent } from './detail/detail.component';

@Component({
  selector: 'app-root',
  templateUrl: ` <div> <app-detail></app-detail> </div> `,
  styleUrls: []
})
export class AppComponent implements AfterViewInit, AfterViewChecked {

  @ViewChild(DetailComponent) detailView: DetailComponent;

  ngAfterViewInit() {
    console.log('after view init');

  }

  ngAfterViewChecked() {
    console.log('after view checked, ', this.detailView);
  }
}
複製代碼
// detail.component.ts
import { Component, OnInit } from '@angular/core';

@Component({
  selector: 'app-detail',
  templateUrl: `<input type="text" [(ngModel)]="innerName">`,
  styleUrls: []
})
export class DetailComponent implements OnInit {

  public innerName = 'yyz';

  constructor() { }

  ngOnInit() {}
}
複製代碼

以上示例中,每次修改子組件中的input框的值,父組件中的ngAfterViewChecked()都會觸發,並能夠獲取子組件中的屬性值。ngAfterViewChecked()也是一個會被頻繁調用的生命週期,因此裏面的代碼邏輯應當儘可能精簡;

另外,若是在ngAfterViewChecked()中直接修改組件屬性,會觸發一個錯誤ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked。有關這個錯誤的具體緣由能夠另外在寫一篇文章,所裏這邊不贅述了。由於Angular自己的單項數據流規則禁止在視圖被組合好以後再更新視圖。afterView的兩個鉤子都是在視圖被組合好以後觸發的。要避免這個錯誤,須要將操做推到瀏覽器下一個event loop。

ngAfterViewChecked() {
    console.log('after view checked, ', this.detailView);
    // this will throw one error
    // this.title = this.detailView.innerName + 'after';
    if (this.detailName !== this.detailView.innerName + 'after') {
      setTimeout(() => {
        this.detailName = this.detailView.innerName + 'after';
      });
    }
}
複製代碼

結束! 哈

相關文章
相關標籤/搜索