Angularjs與Angular對髒檢查機制的理解
“髒檢查”是Angular中的核心機制之一,它是實現雙向資料繫結、MVVM模式的重要基礎。
AngularJS常用函式:$apply,$watch及$digest
$digest是一個內部函式,正常的應用程式碼中是不應該直接呼叫它的。要想主動觸發它,就要呼叫scope.$apply函式,它是觸發Angular“髒檢查機制”的常用公開介面。$digest迴圈實際上包括兩個while迴圈。分別是:處理$evalAsync的非同步運算佇列,處理$watch的watchers佇列。當該迴圈觸發時,它會遍歷當前$scope及其所有子$scope上已註冊的所有watchers函式。遍歷一遍所有watcher函式成為一輪髒檢查。執行完一輪髒檢查,如果任何一個watcher所監聽的值改變過,那麼就會重新再進行一輪髒檢查,直到所有的watcher函式都報告其所監聽的值不改變了。當迴圈結束時,才把模型的變化結果更新到DOM中去。這樣做為了,防止頻繁更新DOM屬性。
通俗理解髒檢查:表示式{{aaa.x}},AngularJS不僅會渲染該資料,還會為該特定值建立一個觀察程式。之後,只要程式發生任何事情,AngularJS就會檢查該觀察過程中的值是否更改。如果有,重新呈現表示式。執行這些觀察者的過程稱為髒檢查。
Angular處理髒檢查引用的是Zone.js
NgZone是一個帶有基於Observables的附加api的分叉區域
想要資料發生變化應用到頁面上,首先需要檢測資料的變化,資料變化一般發生非同步事件中,如:
瀏覽器事件,eg:click,submit
setTimeout和setInterval
XHR,從遠端伺服器獲取資料……
誰通知了Angular?
class ApplicationRef { changeDetectorRefs:ChangeDetectorRef[] = []; // applicationRef在構造器中監聽onTurnDone事件 constructor(private zone: NgZone) { this.zone.onTurnDone .subscribe(() => this.zone.run(() => this.tick())) } // tick函式遍歷所有的探測器的介面/物件 對其執行檢測 tick() { this.changeDetectorRefs .forEach((ref) => ref.detectChanges()); } } }
變更檢測是如何進行的呢?
每一個元件都有屬於自己的變更檢測器(change detector)
變更檢測樹:有向圖 單向資料流 始終都是由上而下執行更改檢測
預設情況下,Angular是保守的,每次都會檢查每個元件
變更檢測策略
enum ChangeDetectionStrategy {
OnPush: 0 // 僅在輸入已更改時才對檢視進行更改檢測。當輸入屬性不變時,Angular可以跳過整個變更檢測樹
Default: 1 // 預設策略,其中更改檢測是自動的,直到明確停用
}
NgDoCheck鉤子和變更檢測
更新子元件的屬性
呼叫位於子元件中的NgDoCheck生命週期鉤子
更新當前元件的DOM
向子元件執行變更檢測
NgDoCheck作用:配合markForCheck和OnPush
export class AppComponent {
@Input() data;
public id;
constructor(private cdr: ChangeDetectorRef){}
ngOnChanges() {
// 當data改變時,更新id
this.id = this.data.id;
}
ngDoCheck() {
// 在ngDoCheck中檢測data這個object的屬性是否更改
if( this.id !== this.data.id ) {
this.cdr.markForCheck();
}
}
}
@Component({
template:'{{num}}'
changeDetection: ChangeDetectionStrategy.OnPush
})
export class NumComponent implements OnInit {
@Input() addItem: Observable<any>;
num = 0;
constructor(private cdr: ChangeDetectorRef){}
ngOnInit() {
this.addItem.subscribe(()=>{
this.num++;
this.cdr.markForCheck();//人為通知Angular進行檢測
})
}
}
Question?
為什麼在OnPush策略下,即使元件沒有屬性更新,ngOnCheck鉤子仍然被呼叫?
存在一種可能是子元件使用了OnPush策略而父元件沒有,子元件的@Input屬性沒有更改,但是ngOnCheck鉤子執行的是當前元件變化的呼叫。