[英]Angular2 change detection misunderstanding - With plunker
I'm trying to fully understand change detection with Angular2 final. 我正在尝试用Angular2 final完全理解变化检测。
This include: 这包括:
I thought I already got a pretty clear overview of those concepts, but to make sure my assumptions where right, I wrote a small plunkr to test them. 我以为我已经对这些概念进行了非常明确的概述,但为了确保我的假设正确,我写了一个小傻瓜来测试它们。
My general understanding about that where globally right, but in some situations, I get a little bit lost. 我对全球正确的一般理解,但在某些情况下,我有点迷失。
Here is the plunker: Angular2 Change detection playground 这是plunker: Angular2变化检测操场
Quick explanation of the plunker: 对plunker的快速解释:
Pretty simple: 很简单:
The parent attribute can be passed to children components by either: parent属性可以通过以下任一方式传递给子组件:
For each child component, ChangeDetector can be attached or detached. 对于每个子组件,可以附加或分离ChangeDetector。 ( "detach()" and "reattach()" buttons)
( “detach()”和“reattach()”按钮)
OnPush child have an additional internal property that can be edited, and change detection can be explicitly applied ( "detectChanges()" button) OnPush子项具有可以编辑的附加内部属性,并且可以显式应用更改检测( “detectChanges()”按钮)
Here are the scenarios where I get behaviours that I cannot explain: 以下是我得到无法解释的行为的场景:
Scenario1: Scenario1:
Expected behavior: I expect BOTH children NOT to be updated, because they both have their change detector detached. 预期的行为:我希望两个孩子都不会被更新,因为他们都有自己的变化探测器。
Current behavior: Default child is not updated, but OnPush child is updated .. WHY? 当前行为:默认子项未更新,但OnPush子项已更新.. 为什么? It shouldn't because its CD is detached ...
它不应该因为它的CD分离...
Scenario2: Scenario2:
Expected behavior: OnPush children should NOT be updated at ALL, once again because its CD is detached...CD should not happen at all on this component 预期的行为: OnPush的孩子不应该全部更新,再次因为它的CD被分离...... CD不应该在这个组件上发生
Current behavior: Both the value and the internal values are updated, seams like a full CD is applied to this component. 当前行为:更新值和内部值,将完整CD等接缝应用于此组件。
Expected behavior: Internal value should NOT be updated because CD is still detached 预期行为:不应更新内部值,因为CD仍然已分离
Current behavior: Internal value change is detected... WHY? 当前行为:检测到内部值变化... 为什么?
Conclusions: 结论:
According to those tests I conclude the following, which seams strange to me: 根据那些测试,我得出以下结论,这对我来说很奇怪:
What do you think about those conclusions? 您如何看待这些结论?
Can you explain this behavior in a better way? 你能以更好的方式解释这种行为吗?
Is this a bug or the desired behavior? 这是一个错误还是想要的行为?
Component with OnPush strategy get 'changed detected' when their input changes, EVEN IF their change detector is detached.
具有OnPush策略的组件在其输入发生变化时会“检测到已更改”,即使它们的变化检测器已分离。
Since Angular 4.1.1 (2017-05-04) OnPush
should respect detach()
自Angular 4.1.1 (2017-05-04)
OnPush
应尊重detach()
https://github.com/angular/angular/commit/acf83b9 https://github.com/angular/angular/commit/acf83b9
There are a lot of undocumented stuff about how change detection works. 关于变更检测如何工作,有很多未记载的内容。
We should be aware about three main changeDetection statuses ( cdMode
): 我们应该知道三个主要的changeDetection状态 (
cdMode
):
1) CheckOnce - 0 1) CheckOnce - 0
CheckedOnce
means that after calling detectChanges the mode of the change detector will becomeChecked
.CheckedOnce
表示在调用detectChanges后,更改检测器的模式将变为Checked
。
AppView class AppView类
detectChanges(throwOnChange: boolean): void {
...
this.detectChangesInternal(throwOnChange);
if (this.cdMode === ChangeDetectorStatus.CheckOnce) {
this.cdMode = ChangeDetectorStatus.Checked; // <== this line
}
...
}
2) Checked - 1 2) 检查 - 1
Checked
means that the change detector should be skipped until its mode changes toCheckOnce
.Checked
表示应跳过更改检测器,直到其模式更改为CheckOnce
。
3) Detached - 3 3) 独立 - 3
Detached
means that the change detector sub tree is not a part of the main tree and should be skipped.Detached
意味着变化检测器子树不是主树的一部分,应该被跳过。
Here are places where Detached
is used 以下是使用
Detached
地方
AppView class AppView类
Skip content checking 跳过内容检查
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);
}
}
Skip view checking 跳过视图检查
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);
}
}
Skip changing cdMode
to CheckOnce
跳过将
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;
}
}
Note: markPathToRootAsCheckOnce
is running in all event handlers of your view: 注意:
markPathToRootAsCheckOnce
在视图的所有事件处理程序中运行:
So if set status to Detached
then your view won't be changed. 因此,如果将状态设置为
Detached
则不会更改您的视图。
Then how works OnPush
strategy 然后如何运作
OnPush
策略
OnPush
means that the change detector's mode will be set toCheckOnce
during hydration.OnPush
意味着更换检测器的模式将在水合过程中设置为CheckOnce
。
compiler/src/view_compiler/property_binder.ts 编译器/ 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 https://github.com/angular/angular/blob/2.1.2/modules/%40angular/compiler/src/view_compiler/property_binder.ts#L193-L197
Let's see how it looks in your example: 让我们看看它在你的例子中的样子:
Parent factory (AppComponent) 父工厂(AppComponent)
And again back to the AppView class : 再次回到AppView类 :
markAsCheckOnce(): void { this.cdMode = ChangeDetectorStatus.CheckOnce; }
Scenario 1 场景1
1) Detach Change detector of OnPush Children and Default Children (click "detach()" on both components)
1)分离OnPush Children和Default Children的Change变换器(点击两个组件上的“detach()”)
OnPush.cdMode - Detached
3) Click "Change obj" to pass the modified attribute to the children
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
Therefore OnPush.dectectChanges
is firing. 因此
OnPush.dectectChanges
正在解雇。
Here is conclusion: 这是结论:
Component with
OnPush
strategy get 'changed detected' when their input changes, EVEN IF their change detector is detached.具有
OnPush
策略的组件在其输入发生变化时会“检测到已更改”,即使它们的变化检测器已分离。 Moreover It changes view's status toCheckOnce
.此外,它将视图的状态更改为
CheckOnce
。
Scenario2 Scenario2
1) Detach CD for the OnPush component
1)为OnPush组件分离CD
OnPush.cdMode - Detached
6) Click "Change obj" to pass the modified attribute to the children
6)单击“更改obj”将修改后的属性传递给子项
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 ...
7)最后一次,编辑内部值输入并单击更改内部:检测到更改,并更新内部值...
As I mentioned above, all event handlers includes markPathToRootAsCheckOnce
. 如上所述,所有事件处理程序都包含
markPathToRootAsCheckOnce
。 So: 所以:
markPathToRootAsCheckOnce
||
\/
OnPush.cdMode - CheckOnce
||
\/
OnPush.detectChanges
||
\/
OnPush.cdMode - Checked
As you can see OnPush strategy and ChangeDetector manage one property - cdMode 正如您所见,OnPush策略和ChangeDetector管理一个属性 - cdMode
Component with OnPush strategy get their change detector re attached each time their input changed ...
具有OnPush策略的组件每次输入更改时都会重新连接其变化检测器...
In conclusion I want to say that seems you're right. 总之,我想说你似乎是对的。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.