最近在做項目的過程中遇到一個問題:在回調(diào)函數(shù)里,數(shù)據(jù)發(fā)生變化時,視圖并沒有相應(yīng)地更新。于是就在網(wǎng)上搜索解決方案,說是將Component中的changeDetection值修改為ChangeDetectionStrategy.OnPush,再在constructor里導(dǎo)入ChangeDetectorRef,最后在數(shù)據(jù)模型發(fā)生變化的地方添加變更檢測的代碼。
@Component({
...
changeDetection: ChangeDetectionStrategy.OnPush
})
export class DemoComponent {
construtor(private cdf: ChangeDetectorRef) {}
changeData() {
... // 回調(diào)函數(shù)
this.data = {a: 1}; //變化的數(shù)據(jù)
this.cdf.markForCheck(); // 進(jìn)行標(biāo)注
this.cdf.detectChanges(); // 要多加一行這個 執(zhí)行一次變化檢測
}
}
這樣一來,修改后的數(shù)據(jù)更新在視圖上,問題得到解決。
但是changeDetection是什么? 為什么加上changeDetection相關(guān)內(nèi)容后,數(shù)據(jù)就會更新在視圖上?沒加之前,為什么有的數(shù)據(jù)更新了,有的沒有更新呢?等等一系列的問題接踵而至。
問題提出來了,現(xiàn)在要做的就是去解決問題。
What
changeDetection是什么呢?它是Angular的一個特性。當(dāng)組件中的數(shù)據(jù)發(fā)生變化時,Angular自動檢測到數(shù)據(jù)變化并更新相應(yīng)的視圖。
一般情況下,將數(shù)據(jù)的狀態(tài)以按鈕、表單、鏈接或者圖片形式呈現(xiàn)在視圖上,也就是DOM。所以,我們將數(shù)據(jù)結(jié)構(gòu)作為輸入并生成DOM顯示給用戶這一過程稱之為渲染。
Why
上文中提到數(shù)據(jù)變化,那數(shù)據(jù)為什么會變化呢?
一般情況下,在進(jìn)行異步的操作之后,數(shù)據(jù)就會發(fā)生變化。一般有三種情況:
- Events: click、submit、onmouseup...
- XHR請求: 從服務(wù)器獲取數(shù)據(jù)
- Timers: setTimeout()、setInterval()
這些都是異步的,所以,有異步操作執(zhí)行,數(shù)據(jù)就會發(fā)生變化,也是在這個時候通知Angular更新視圖。
Who
剛才說通知Angular更新視圖,那誰來通知Angular呢?NgZone來通知Angular更新視圖。
How
以上,我們了解到ChangeDeteciton是如何觸發(fā)的,但是怎么運行的呢?目前,我們所需要注意到的是:每個組件都有它自己的變更檢測器。
設(shè)想一個場景:
在一個組件中,一個按鈕被點擊,接下來會發(fā)生什么呢,當(dāng)有數(shù)據(jù)發(fā)生變化時,Ngzone會做出處理并且通知Angular,最終Angular會執(zhí)行變更檢測。
正如我們所知的,每個組件有它自己的變更檢測器,一個Angular應(yīng)用包含一個組件樹,因此推斷出一個angular應(yīng)用也有一個變更檢測樹。這個樹可以視為有向圖,其中,數(shù)據(jù)始終從頂部流向底部。

數(shù)據(jù)從頂部流向底部的原因時因為每個單獨的組件,從根組件開始,每個組件也始終從上到下執(zhí)行變更檢測。這樣的話,相比循環(huán)數(shù)據(jù)流,單向數(shù)據(jù)流更容易預(yù)測。我們總是知道數(shù)據(jù)來自何處,因為它只能來自其組件。