DOM 正在更改检测周期中为具有 OnPush 策略的视图更新,即使输入未更改也是如此
DOM is getting updated for a view with OnPush strategy during change detection cycle even though Input is not changed
也许我在 Angular 的变更检测机制中遗漏了一些东西。
我有两个组件:
AppComponent.
@Component({
selector: 'my-app',
template: `
<button
(click)="0">
Async Action in Parent
</button>
<child
[config]="config">
</child>
<button
(click)="mutate()">
Mutate Input Object
</button>
`
})
export class AppComponent {
config = {
state: 0
};
mutate() {
this.config.state = 1;
console.log(this.config.state);
}
}
和子组件
@Component({
selector: "child",
template: `
<h1>{{config.state}}</h1>
<button
(click)="0">
Async Action in Child
</button>
`,
changeDetection: ChangeDetectionStrategy.OnPush
})
export class ChildComponent {
@Input() config;
ngDoCheck() {
console.log(`Check`);
}
}
Step 1.
在这里,在单击 AppComponent
中的按钮时,我调用 mutate
函数,该函数反过来改变传递给嵌套 ChildComponent
.
的对象
Step 2.
然后我单击同一个 AppComponent
中的另一个按钮,只是为了触发 Change Detection 循环。由于 ChildComponent
正在使用 OnPush
策略并且输入参考没有更改,因此在接下来的 变更检测 期间 ChildComponent
的视图是 未检查 和 DOM 插值未更新。到目前为止,它的行为符合我的预期。
但是如果在 步骤 2. 我从 [=15] 中触发 变更检测 =](通过单击适当的按钮)然后 ChildComponent
的视图被 选中 并且 DOM 插值被 更新 。为什么会这样?自上次以来没有任何变化。
Here 是 StackBlitz 上的演示。
我读过 great article on Change Detection by Maxim Koretskyi 但不幸的是它没有回答我的问题。
在组件中触发 onpush 变化检测的事情(除了输入变化)包括输出事件,这就是点击指令绑定,以及所有其他用户交互指令绑定(滚动、鼠标悬停等)。组件和异步管道中的超时、间隔或承诺解决方案也是如此。 Angular 无法知道输出事件或其他任何东西是否实际改变了什么,所以它假设它改变了并运行一个变化检测周期。
将变化检测周期想象成如下:
- 有些东西需要发出信号 Angular 以检查视图中是否有任何变化,
- 当Angular收到这样的信号时,Angular将检查视图中是否有任何变化。
因此,正如 bryan60 指出的那样,有许多事件会发出信号 Angular 在使用 OnPush
时检查视图:@Output 事件、点击事件、滚动事件、鼠标悬停事件、等等...我将在此列表中指出的另一个事件是对@Input 值的更改。正如您已经指出的那样,Angular 在 config
对象发生突变时没有检查视图,因为突变不是实际的变化。因此 @Input 没有触发 Angular 检查视图。然而,随后的点击事件确实发出 Angular 检查视图的信号。现在,我认为重要的部分来了:一旦 Angular 收到检查视图的信号,它会检查 视图绑定 , 而不是 @Input 值。换句话说,点击事件导致 Angular 检查 {{ config.state }}
是否改变,而不是 @Input 值(即配置)是否改变。
也许我在 Angular 的变更检测机制中遗漏了一些东西。
我有两个组件:
AppComponent.
@Component({
selector: 'my-app',
template: `
<button
(click)="0">
Async Action in Parent
</button>
<child
[config]="config">
</child>
<button
(click)="mutate()">
Mutate Input Object
</button>
`
})
export class AppComponent {
config = {
state: 0
};
mutate() {
this.config.state = 1;
console.log(this.config.state);
}
}
和子组件
@Component({
selector: "child",
template: `
<h1>{{config.state}}</h1>
<button
(click)="0">
Async Action in Child
</button>
`,
changeDetection: ChangeDetectionStrategy.OnPush
})
export class ChildComponent {
@Input() config;
ngDoCheck() {
console.log(`Check`);
}
}
Step 1.
在这里,在单击 AppComponent
中的按钮时,我调用 mutate
函数,该函数反过来改变传递给嵌套 ChildComponent
.
Step 2.
然后我单击同一个 AppComponent
中的另一个按钮,只是为了触发 Change Detection 循环。由于 ChildComponent
正在使用 OnPush
策略并且输入参考没有更改,因此在接下来的 变更检测 期间 ChildComponent
的视图是 未检查 和 DOM 插值未更新。到目前为止,它的行为符合我的预期。
但是如果在 步骤 2. 我从 [=15] 中触发 变更检测 =](通过单击适当的按钮)然后 ChildComponent
的视图被 选中 并且 DOM 插值被 更新 。为什么会这样?自上次以来没有任何变化。
Here 是 StackBlitz 上的演示。
我读过 great article on Change Detection by Maxim Koretskyi 但不幸的是它没有回答我的问题。
在组件中触发 onpush 变化检测的事情(除了输入变化)包括输出事件,这就是点击指令绑定,以及所有其他用户交互指令绑定(滚动、鼠标悬停等)。组件和异步管道中的超时、间隔或承诺解决方案也是如此。 Angular 无法知道输出事件或其他任何东西是否实际改变了什么,所以它假设它改变了并运行一个变化检测周期。
将变化检测周期想象成如下:
- 有些东西需要发出信号 Angular 以检查视图中是否有任何变化,
- 当Angular收到这样的信号时,Angular将检查视图中是否有任何变化。
因此,正如 bryan60 指出的那样,有许多事件会发出信号 Angular 在使用 OnPush
时检查视图:@Output 事件、点击事件、滚动事件、鼠标悬停事件、等等...我将在此列表中指出的另一个事件是对@Input 值的更改。正如您已经指出的那样,Angular 在 config
对象发生突变时没有检查视图,因为突变不是实际的变化。因此 @Input 没有触发 Angular 检查视图。然而,随后的点击事件确实发出 Angular 检查视图的信号。现在,我认为重要的部分来了:一旦 Angular 收到检查视图的信号,它会检查 视图绑定 , 而不是 @Input 值。换句话说,点击事件导致 Angular 检查 {{ config.state }}
是否改变,而不是 @Input 值(即配置)是否改变。