6、Angular 2元件的生命週期鉤子
如果打算自己寫元件庫的話,元件的宣告週期需要深入掌握。尋常的業務程式碼倒不會太涉及這方面的東西。
元件生命週期的這些鉤子的執行順序
如下圖所示:
實現的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();
}
}