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

數(shù)據(jù)從頂部流向底部的原因時(shí)因?yàn)槊總€(gè)單獨(dú)的組件,從根組件開始,每個(gè)組件也始終從上到下執(zhí)行變更檢測。這樣的話,相比循環(huán)數(shù)據(jù)流,單向數(shù)據(jù)流更容易預(yù)測。我們總是知道數(shù)據(jù)來自何處,因?yàn)樗荒軄碜云浣M件。