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 无法知道输出事件或其他任何东西是否实际改变了什么,所以它假设它改变了并运行一个变化检测周期。

将变化检测周期想象成如下:

  1. 有些东西需要发出信号 Angular 以检查视图中是否有任何变化,
  2. 当Angular收到这样的信号时,Angular将检查视图中是否有任何变化。

因此,正如 bryan60 指出的那样,有许多事件会发出信号 Angular 在使用 OnPush 时检查视图:@Output 事件、点击事件、滚动事件、鼠标悬停事件、等等...我将在此列表中指出的另一个事件是对@Input 值的更改。正如您已经指出的那样,Angular 在 config 对象发生突变时没有检查视图,因为突变不是实际的变化。因此 @Input 没有触发 Angular 检查视图。然而,随后的点击事件确实发出 Angular 检查视图的信号。现在,我认为重要的部分来了:一旦 Angular 收到检查视图的信号,它会检查 视图绑定 而不是 @Input 值。换句话说,点击事件导致 Angular 检查 {{ config.state }} 是否改变,而不是 @Input 值(即配置)是否改变。