这包括:
>处理变更检测策略
>从组件上安装和拆卸更换检测器.
我以为我已经对这些概念有了非常明确的概述,但为了确保我的假设正确,我写了一个小傻瓜来测试它们.
我对全球正确的一般理解,但在某些情况下,我有点迷失.
这是羽毛球: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 becomeChecked
.
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 toCheckOnce
.
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 toCheckOnce
during hydration.
编译器/ 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) 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 toCheckOnce
.
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 …
总之,我想说,你似乎是对的.