Angular2变化检测误解 – 用plunker

前端之家收集整理的这篇文章主要介绍了Angular2变化检测误解 – 用plunker前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
我正在尝试用Angular2 final完全理解变化检测.

包括

>处理变更检测策略
>从组件上安装和拆卸更换检测器.

我以为我已经对这些概念有了非常明确的概述,但为了确保我的假设正确,我写了一个小傻瓜来测试它们.

我对全球正确的一般理解,但在某些情况下,我有点迷失.

这是羽毛球:Angular2 Change detection playground

关于plunker的快速说明:

很简单:

>一个父组件,您可以在其中编辑一个将传递给两个子组件的属性
>将具有变化检测策略的儿童设置为OnPush
>将具有更改检测策略的子项设置为“默认”

parent属性可以通过以下任一方式传递给子组件:

>更改整个属性对象,并创建一个新对象(“更改obj”按钮)(触发OnPush子项上的更改检测)
>更改属性对象内的成员(“更改内容”按钮)(不会触发OnPush子项上的更改检测)

对于每个子组件,可以附加或分离ChangeDetector. (“detach()”和“reattach()”按钮)

OnPush孩子有一个可以编辑的额外内部属性,
并且可以明确应用更改检测(“detectChanges()”按钮)

以下是我得到无法解释的行为的场景:

Scenario1:

>分离OnPush Children和Default Children的更改检测器(单击两个组件上的“detach()”)
>编辑父属性firstname和lastname
>单击“更改obj”将修改后的属性传递给子项

预期行为:
我希望两个孩子都不要更新,因为他们都更换了变换探测器.

目前的行为:
默认子项未更新,但OnPush子项已更新..为什么?
它不应该因为它的CD分离…

Scenario2:

>为OnPush组件分离CD
>编辑其内部值输入并单击更改内部:没有任何反应,因为CD已分离,因此未检测到更改…确定
>单击detectChanges():检测到更改并更新视图.到现在为止还挺好.
>再次编辑内部值输入并单击更改内部:再次,没有任何反应,因此未检测到更改..确定
>编辑父属性firstname和lastname.
>单击“更改obj”将修改后的属性传递给子项

预期行为:
OnPush的孩子不应该全部更新,因为它的CD已经分离…… CD不应该在这个组件上发生

目前的行为:
值和内部值都会更新,像完整CD这样的接缝会应用于此组件.

>最后一次,编辑内部值输入并单击更改内部:检测到更改,并更新内部值…

预期行为:
不应更新内部值,因为CD仍然已分离

目前的行为:
检测到内部价值变化……为什么?

结论:

根据那些测试,我得出以下结论,这对我来说很奇怪:

>具有OnPush策略的组件在输入更改时会“检测到已更改”,即使它们的更改检测器已分离.
>具有OnPush策略的组件每次输入更改时都会重新连接其变化检测器…

您如何看待这些结论?

你能以更好的方式解释这种行为吗?

这是一个错误还是想要的行为?

更新

Component with OnPush strategy get ‘changed detected’ when their input
changes,EVEN IF their change detector is detached.

自Angular 4.1.1(2017-05-04)OnPush应该尊重detach()

https://github.com/angular/angular/commit/acf83b9

旧版

关于变更检测如何工作,有很多未记载的内容.

我们应该知道三个主要的changeDetection状态(cdMode):

1)CheckOnce – 0

CheckedOnce means that after calling detectChanges the mode of the
change detector will become Checked.

AppView类

detectChanges(throwOnChange: boolean): void {
  ...
  this.detectChangesInternal(throwOnChange);
  if (this.cdMode === ChangeDetectorStatus.CheckOnce) {
    this.cdMode = ChangeDetectorStatus.Checked; // <== this line
  }
  ...
}

2)检查 – 1

Checked means that the change detector should be skipped until its mode changes to CheckOnce.

3)独立 – 3

Detached means that the change detector sub tree is not a part of
the main tree and should be skipped.

以下是使用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 means that the change detector’s mode will be set to CheckOnce
during hydration.

编译器/ 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) Detach Change detector of OnPush Children and Default Children (click “detach()” on both components)

OnPush.cdMode - Detached

3) Click “Change obj” to pass the modified attribute to the children

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正在解雇.

结论如下:

Component with OnPush strategy get ‘changed detected’ when their
input changes,EVEN IF their change detector is detached. Moreover
It changes view’s status to CheckOnce.

Scenario2

1) Detach CD for the OnPush component

OnPush.cdMode - Detached

6) Click “Change obj” to pass the modified attribute to the
children

See 3) from scenario 1 => OnPush.cdMode - Checked

7) For the last time,edit the internal value input and click change
internal: Change is detected,and internal value is updated …

如上所述,所有事件处理程序都包含markPathToRootAsCheckOnce.所以:

markPathToRootAsCheckOnce
        ||
        \/
OnPush.cdMode - CheckOnce
        ||
        \/
OnPush.detectChanges
        ||
        \/
OnPush.cdMode - Checked

正如您所见,OnPush策略和ChangeDetector管理一个属性 – cdMode

Component with OnPush strategy get their change detector re attached
each time their input changed …

总之,我想说,你似乎是对的.

猜你在找的Angularjs相关文章