angular - ChangeDetectionStrategy.OnPush 不符合我的预期

标签 angular typescript

我正在尝试熟悉 Angular 2 的 ChangeDetectionStrategy.OnPush 性能提升(如 here 所述)。但我这里有古玩盒。

我有父 AppComponent:

@Component({
  selector: 'my-app',
  template: `<h1>
  <app-literals [title]="title" [dTitle]="dTitle"></app-literals>
  <input [value]="title.name"/>
</h1>
`
})
export class AppComponent implements OnInit {
  title = { name: 'original' };
  dTitle = { name: "original" };

  constructor(private changeDetectorRef : ChangeDetectorRef) {

  }

    ngOnInit(): void {
      setTimeout(() => {
        alert("About to change");
        this.title.name = "changed";
        this.dTitle = { name: "changed" };
      }, 1000);
    }

}

和子 LiteralsComponent 组件:

@Component({
  selector: 'app-literals',
  template: `  {{title.name}}
  {{dTitle.name}}`,
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class LiteralsComponent implements OnInit {
  @Input('title') title;
  @Input('dTitle') dTitle;

  constructor() { }

  ngOnInit() {
  }

}

我认为将策略设置为 OnPush 会使 Angular 仅反射(reflect)引用的更改,但在示例中我尝试更改(变异)对象的属性, Angular 仍然反射(reflect)它。

this.title.name = "changed"; 不应被检测到(因此 UI 不应不反射(reflect)更改)。

这是关于 plunker 的案例

怎么来的?怎样做才是对的?

最佳答案

如果我没理解错,你问的是为什么绑定(bind)值在 LiteralsComponent 模板中更新,即使你不修改引用 title,而是改变对象.

简短的回答是因为您修改了两者:

this.title.name = "changed";
this.dTitle = {name: "changed"};

AppComponent.ngOnInit 中。如果您只修改 this.title.name = "changed",您将看到该模板没有更新。

然而,这是一个非常有趣的问题,需要详细探讨

让我们首先从没有 this.dTitlethis.title 开始。
首先要了解的是,当您在模板中指定以下内容时:

{{title.name}}

这是 Angular 所做的。它尝试在当前组件实例上找到 title 对象,然后从中获取 name 属性并将其反射(reflect)在 DOM 中。但具有以下配置:

class AppComponent {
    title = { name: 'original' }

    ngOnInit(): void {
      setTimeout(() => {
        alert("About to change");
       this.title.name = "changed";
    }, 1000);
}
}

class LiteralsComponent {
     @Input() title;
}

title 对象在两个组件中相同(指向相同的内存位置)。

因此,当 Angular 为 LiteralsComponent 组件运行更改检测时,它会访问您在此处在 AppComponent 中更改的同一对象:

ngOnInit(): void {
  setTimeout(() => {
    alert("About to change");
    this.title.name = "changed";
  }, 1000);
}

这里有趣的观察是变化未被检测到,无论是使用 OnPush 还是没有它:

class LiteralsComponent {
     @Input() title;

     ngOnChanges(changes) {
         // will be triggered only for the first CD cycle,
         // and won't be triggered when `title` is updated
     }
}

现在,最后一件事是了解 DOM 何时更新。根据this article , 它在当前组件的 CD 期间更新。这意味着如果未选中当前组件,则不会更新 DOM。所以我们为 LiteralsComponent 指定 onPush:

changeDetection: ChangeDetectionStrategy.OnPush,

View 不会更新。

但是,它已在您的问题中更新。为什么?

这就是 dTitle 发挥作用的地方。使用此属性,您实际上是在修改引用,Angular 检测绑定(bind)更改 并为 LiteralsComponent 组件运行 CD。我们在上面了解到,当 CD 运行时,DOM 会更新。所以 Angular 也会更新 {{title.name}},因为它指向 AppComponent 中的同一个对象,虽然它没有检测到它被改变了.

关于angular - ChangeDetectionStrategy.OnPush 不符合我的预期,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43539254/

相关文章:

javascript - 检查型号 "dirtiness"

typescript - 类型 'IArguments' 不是数组类型

angular - 捕获模板中使用的第三方指令的异常

javascript - 使用 Riot-ts (Riotjs) 在 Typescript 模块中进行多个匿名定义

typescript - 如何在 TypeScript 声明中使用 Webpack 别名

angular - 在没有服务器的情况下在浏览器中将 Angular2 作为静态应用程序运行

angular - Karma formGroup 需要一个 FormGroup 实例。请传一个进来

c# - 将 TypeScript 编译器加载到 ClearScript, "WScript is undefined",不可能的任务?

javascript - 在 Typescript Enum 中使用整数作为值?

javascript - 以 Angular 形式显示订阅响应值