簡體   English   中英

Angular2變化檢測誤解 - 用plunker

[英]Angular2 change detection misunderstanding - With plunker

我正在嘗試用Angular2 final完全理解變化檢測。

這包括:

  • 處理變更檢測策略
  • 從組件上安裝和拆卸更換檢測器。

我以為我已經對這些概念進行了非常明確的概述,但為了確保我的假設正確,我寫了一個小傻瓜來測試它們。

我對全球正確的一般理解,但在某些情況下,我有點迷失。


這是plunker: Angular2變化檢測操場

對plunker的快速解釋:

很簡單:

  • 一個父組件,您可以在其中編輯一個將傳遞給兩個子組件的屬性:
  • 將具有變化檢測策略的孩子設置為OnPush
  • 將具有更改檢測策略的子項設置為“默認”

parent屬性可以通過以下任一方式傳遞給子組件:

  • 更改整個屬性對象,並創建一個新對象(“ 更改obj ”按鈕)(觸發OnPush子項上的更改檢測)
  • 更改屬性對象內的成員( “更改內容”按鈕)(不會觸發OnPush子項上的更改檢測)

對於每個子組件,可以附加或分離ChangeDetector。 “detach()”“reattach()”按鈕)

OnPush子項具有可以編輯的附加內部屬性,並且可以顯式應用更改檢測( “detectChanges()”按鈕)


以下是我得到無法解釋的行為的場景:

Scenario1:

  1. 分離OnPush Children和Default Children的更改檢測器(單擊兩個組件上的“ detach() ”)
  2. 編輯父屬性firstname和lastname
  3. 單擊“ 更改obj ”將修改后的屬性傳遞給子項

預期的行為:我希望兩個孩子都不會被更新,因為他們都有自己的變化探測器。

當前行為:默認子項未更新,但OnPush子項已更新.. 為什么? 它不應該因為它的CD分離...

Scenario2:

  1. 為OnPush組件分離CD
  2. 編輯其內部值輸入並單擊更改內部 :沒有任何反應,因為CD已分離,因此未檢測到更改...確定
  3. 單擊detectChanges() :檢測到更改並更新視圖。 到現在為止還挺好。
  4. 再一次,編輯內部值輸入並單擊更改內部 :再次,沒有任何事情發生,因為CD已分離,因此未檢測到更改..確定
  5. 編輯父屬性firstname和lastname。
  6. 單擊“ 更改obj ”將修改后的屬性傳遞給子項

預期的行為: OnPush的孩子不應該全部更新,再次因為它的CD被分離...... CD不應該在這個組件上發生

當前行為:更新值和內部值,將完整CD等接縫應用於此組件。

  1. 最后一次,編輯內部值輸入並單擊更改內部 :檢測到更改,並更新內部值...

預期行為:不應更新內部值,因為CD仍然已分離

當前行為:檢測到內部值變化... 為什么?


結論:

根據那些測試,我得出以下結論,這對我來說很奇怪:

  • 具有OnPush策略的組件在其輸入發生變化時會“檢測到已更改”, 即使它們的變化檢測器已分離。
  • 具有OnPush策略的組件每次輸入更改時都會重新連接其變化檢測器...

您如何看待這些結論?

你能以更好的方式解釋這種行為嗎?

這是一個錯誤還是想要的行為?

更新

具有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();

https://github.com/angular/angular/blob/2.1.2/modules/%40angular/compiler/src/view_compiler/property_binder.ts#L193-L197

讓我們看看它在你的例子中的樣子:

父工廠(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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM