更新
具有 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 的更改检测器(单击两个组件上的“detach()”)
OnPush.cdMode - Detached
3)点击“Change 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
。
情景2
1) 为 OnPush 组件分离 CD
OnPush.cdMode - Detached
6)点击“Change obj”将修改后的属性传递给孩子
See 3) from scenario 1 => OnPush.cdMode - Checked
7)最后一次,编辑内部值输入并单击更改内部:检测到更改,更新内部值...
正如我上面提到的,所有事件处理程序都包括markPathToRootAsCheckOnce
. 所以:
markPathToRootAsCheckOnce
||
\/
OnPush.cdMode - CheckOnce
||
\/
OnPush.detectChanges
||
\/
OnPush.cdMode - Checked
如您所见,OnPush 策略和 ChangeDetector 管理一个属性 - cdMode
每次输入更改时,具有 OnPush 策略的组件都会重新附加更改检测器...
总之,我想说这似乎你是对的。