1. 程式人生 > >6、Angular 2元件的生命週期鉤子

6、Angular 2元件的生命週期鉤子

如果打算自己寫元件庫的話,元件的宣告週期需要深入掌握。尋常的業務程式碼倒不會太涉及這方面的東西。

photo1

這裡寫圖片描述

元件生命週期的這些鉤子的執行順序
如下圖所示:
image

實現的ngOnChanges(){}鉤子方法會在元件的最先執行,比ngOnInit()都更前。

這和angular元件本身的例項化是有關係的。angular在例項化元件時是會先去處理那些輸入屬性(也就是@Input屬性)的。所以會先呼叫ngOnChanges()。

一些程式碼我們既能放在constructor方法中,也能放在ngOnInit方法中。但是要注意在constructor方法中最好只做一些簡單的賦值,不要做重複的邏輯

。獲取服務端資料、業務邏輯等不要實現在建構函式中。

ngOnInit

使用angular/cli的元件生成命令時,會自動在元件程式碼中生成ngOnInit的鉤子,表明這個肯定時用得比較多的:

// 這個方法是在OnInit這個介面類中宣告的,所以元件類都需要implements這個介面類
// 另外如果要實現ngOnChanges生命週期鉤子時,也需要implements對應的OnChanges然後實現對應的方法,這些介面類都是都是在angular/core中的  
// 所以需要在元件類的最前方匯入:import { Component, OnInit, OnChanges } from '@angular/core';
// 當然,不寫import也不寫implements也是可以的,只要實現了對應的方法,angular都是認的 ngOnInit() { }

ngOnChanges

  這個鉤子只有在@Input屬性發生變化時才會被呼叫(而且要求屬性是不可變資料型別的時候才會被呼叫,否則也不會被呼叫的。)
  string就是一個典型的不可變資料型別,當一個string型別的屬性變化之後,它不是直接改屬性的值的,而是會新建立一個string然後重新賦給屬性,而之前的那個string可能會被垃圾回收掉可能會遊離狀態或者怎麼樣我們不清楚。
  而JavaScript物件就不是這樣的不可變資料型別,如果你修改了JavaScript物件的某個屬性值,它是不會重新建立一個JavaScript物件進行重新賦值的。所以當@Input屬性是一個JavaScript物件,而且這個物件的一個屬性發生變化時是不會呼叫ngOnChanges的,只會呼叫ngDoCheck。

非@Input屬性改變不會呼叫ngOnChanges

ngDoCheck

  • 每發生一次變更就會被呼叫一次
  • 誰在負責觸發變更呢?(Zones,它攔截了所有回撥:定時器、事件、Ajax)
  • 不要在ngDoCheck裡面做非常消耗效能的事情,會卡死的。

ChangeDetectionStrategy–變更檢測策略

分兩種:
- ChangeDetectionStrategy.OnPush
- ChangeDetectionStrategy.Default

變更檢測的兩種策略
- Default:預設是無論哪個元件發生了變化,從根元件開始全域性遍歷呼叫ngDoCheck()。
- OnPush:這種策略情況下,只有當元件的@Input屬性發生變化的時候才呼叫本元件的ngDoCheck()。

修改元件髒檢查策略的方法:在元件類程式碼的@Component裝飾器中新增changeDetection配置專案(注意在import中也要匯入ChangeDetectionStrategy):

@Component({
    selector: '',
    templateUrl: '',
    styleUrls: ['',''],
    changeDetection:ChangeDetectionStrategy.OnPush
})

ngContent – 內容投影

在子元件中使用<ng-content></ng-content>佔坑,然後在子元件使用時,內部再加一部分html程式碼,這樣執行之後,寫在子元件內部的html程式碼內容就會顯示在ng-content標籤的位置:

<!--父元件中使用子元件時,往內部再加html程式碼-->
...
<child>
    <p>這些東西將會替代子元件html模板中的ng-content標籤</p>
</child>
...


<!--然後子元件的html程式碼中使用ng-content標籤就可以了-->
...
<ng-content></ng-content>
...

跟這個東西相關就產生了兩個鉤子:

ngAfterContentInit && ngAfterContentChecked

父子元件都實現這兩個鉤子的情況下,會先呼叫父層的兩個鉤子,然後再呼叫子層的兩個鉤子,順序如下所示:
1. 父層>ngAfterContentInit
2. 父層>ngAfterContentChecked(會重複呼叫)
3. 子層>ngAfterContentInit
4. 子層>ngAfterContentChecked(會重複呼叫)

後面的關於view的是相反的執行順序。

**投影內容裝配完成的時候整個模板還沒有裝配完
所以,在這兩個鉤子裡面可以修改被繫結的屬性**

ngAfterViewinit && ngAfterViewChecked

當父子元件都實現這兩個鉤子的情況下,會先呼叫子層再呼叫父層,與content是相反的,執行順序如下所示:
1. 子層>ngAfterViewInit
2. 子層>ngAfterViewChecked(會重複呼叫)
3. 父層>ngAgterViewInit
4. 父層>ngAfterViewChecked(會重複呼叫)

在元件檢視裝配的時候呼叫這兩個鉤子
檢視的裝配過程是從子元件向父元件一次進行的。
在這兩個鉤子裡面不能再修改元件上被繫結的屬性,否則Angular會拋異常。
ngAfterViewChecked可能會被呼叫非常多次,如果沒有使用OnPush策略,所有實現了這個鉤子的元件都會被呼叫,千萬不要在這兩個鉤子裡面做很複雜的事情,會被卡死的

@ViewChild

後面講一講,怎樣在父元件的ts程式碼中獲取子元件的元件類物件,就是通過@ViewChild(“child1”)這樣的方式:

父元件中的html檔案使用子元件的方法,使用#進行模板區域性變數宣告:

...
<child1 #child1></child1>
<child2 #child2></child2>
...

然後再父元件類檔案ts程式碼中使用@ViewCheck()裝飾器通過獲取區域性變數名稱來獲取對應的子元件類例項:

export class ParentComponent implements OnInit {
    // 使用@ViewChild("元件區域性變數名稱")裝飾器來獲取子元件類的例項
    @ViewChild("child1")
    child1:Child1Component

    @ViewChild("child2")
    child2:Child2Component

    ...

    ngOnInit() {
        //然後在程式碼中可以直接通過子元件類例項呼叫子元件的方法
        this.child1.aFunction();
    }
}