[英]Angular2 change detection misunderstanding - With plunker
我正在嘗試用Angular2 final完全理解變化檢測。
這包括:
我以為我已經對這些概念進行了非常明確的概述,但為了確保我的假設正確,我寫了一個小傻瓜來測試它們。
我對全球正確的一般理解,但在某些情況下,我有點迷失。
這是plunker: Angular2變化檢測操場
對plunker的快速解釋:
很簡單:
parent屬性可以通過以下任一方式傳遞給子組件:
對於每個子組件,可以附加或分離ChangeDetector。 ( “detach()”和“reattach()”按鈕)
OnPush子項具有可以編輯的附加內部屬性,並且可以顯式應用更改檢測( “detectChanges()”按鈕)
以下是我得到無法解釋的行為的場景:
Scenario1:
預期的行為:我希望兩個孩子都不會被更新,因為他們都有自己的變化探測器。
當前行為:默認子項未更新,但OnPush子項已更新.. 為什么? 它不應該因為它的CD分離...
Scenario2:
預期的行為: OnPush的孩子不應該全部更新,再次因為它的CD被分離...... CD不應該在這個組件上發生
當前行為:更新值和內部值,將完整CD等接縫應用於此組件。
預期行為:不應更新內部值,因為CD仍然已分離
當前行為:檢測到內部值變化... 為什么?
結論:
根據那些測試,我得出以下結論,這對我來說很奇怪:
您如何看待這些結論?
你能以更好的方式解釋這種行為嗎?
這是一個錯誤還是想要的行為?
具有OnPush策略的組件在其輸入發生變化時會“檢測到已更改”,即使它們的變化檢測器已分離。
自Angular 4.1.1 (2017-05-04) OnPush
應尊重detach()
https://github.com/angular/angular/commit/acf83b9
關於變更檢測如何工作,有很多未記載的內容。
我們應該知道三個主要的changeDetection狀態 ( cdMode
):
1) CheckOnce - 0
CheckedOnce
表示在調用detectChanges后,更改檢測器的模式將變為Checked
。
AppView類
detectChanges(throwOnChange: boolean): void {
...
this.detectChangesInternal(throwOnChange);
if (this.cdMode === ChangeDetectorStatus.CheckOnce) {
this.cdMode = ChangeDetectorStatus.Checked; // <== this line
}
...
}
2) 檢查 - 1
Checked
表示應跳過更改檢測器,直到其模式更改為CheckOnce
。
3) 獨立 - 3
Detached
意味着變化檢測器子樹不是主樹的一部分,應該被跳過。
以下是使用Detached
地方
AppView類
跳過內容檢查
detectContentChildrenChanges(throwOnChange: boolean) {
for (var i = 0; i < this.contentChildren.length; ++i) {
var child = this.contentChildren[i];
if (child.cdMode === ChangeDetectorStatus.Detached) continue; // <== this line
child.detectChanges(throwOnChange);
}
}
跳過視圖檢查
detectViewChildrenChanges(throwOnChange: boolean) {
for (var i = 0; i < this.viewChildren.length; ++i) {
var child = this.viewChildren[i];
if (child.cdMode === ChangeDetectorStatus.Detached) continue; // <== this line
child.detectChanges(throwOnChange);
}
}
跳過將cdMode
更改為CheckOnce
markPathToRootAsCheckOnce(): void {
let c: AppView<any> = this;
while (isPresent(c) && c.cdMode !== ChangeDetectorStatus.Detached) { // <== this line
if (c.cdMode === ChangeDetectorStatus.Checked) {
c.cdMode = ChangeDetectorStatus.CheckOnce;
}
let parentEl =
c.type === ViewType.COMPONENT ? c.declarationAppElement : c.viewContainerElement;
c = isPresent(parentEl) ? parentEl.parentView : null;
}
}
注意: markPathToRootAsCheckOnce
在視圖的所有事件處理程序中運行:
因此,如果將狀態設置為Detached
則不會更改您的視圖。
然后如何運作OnPush
策略
OnPush
意味着更換檢測器的模式將在水合過程中設置為CheckOnce
。
編譯器/ SRC / view_compiler / property_binder.ts
const directiveDetectChangesStmt = isOnPushComp ?
new o.IfStmt(directiveDetectChangesExpr, [compileElement.appElement.prop('componentView')
.callMethod('markAsCheckOnce', [])
.toStmt()]) : directiveDetectChangesExpr.toStmt();
讓我們看看它在你的例子中的樣子:
父工廠(AppComponent)
再次回到AppView類 :
markAsCheckOnce(): void { this.cdMode = ChangeDetectorStatus.CheckOnce; }
場景1
1)分離OnPush Children和Default Children的Change變換器(點擊兩個組件上的“detach()”)
OnPush.cdMode - Detached
3)單擊“更改obj”將修改后的屬性傳遞給子項
AppComponent.detectChanges
||
\/
//if (self._OnPush_35_4.detectChangesInInputProps(self,self._el_35,throwOnChange)) {
// self._appEl_35.componentView.markAsCheckOnce();
//}
OnPush.markAsCheckOnce
||
\/
OnPush.cdMode - CheckOnce
||
\/
OnPush.detectChanges
||
\/
OnPush.cdMode - Checked
因此OnPush.dectectChanges
正在解雇。
這是結論:
具有
OnPush
策略的組件在其輸入發生變化時會“檢測到已更改”,即使它們的變化檢測器已分離。 此外,它將視圖的狀態更改為CheckOnce
。
Scenario2
1)為OnPush組件分離CD
OnPush.cdMode - Detached
6)單擊“更改obj”將修改后的屬性傳遞給子項
See 3) from scenario 1 => OnPush.cdMode - Checked
7)最后一次,編輯內部值輸入並單擊更改內部:檢測到更改,並更新內部值...
如上所述,所有事件處理程序都包含markPathToRootAsCheckOnce
。 所以:
markPathToRootAsCheckOnce
||
\/
OnPush.cdMode - CheckOnce
||
\/
OnPush.detectChanges
||
\/
OnPush.cdMode - Checked
正如您所見,OnPush策略和ChangeDetector管理一個屬性 - cdMode
具有OnPush策略的組件每次輸入更改時都會重新連接其變化檢測器...
總之,我想說你似乎是對的。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.