ChangeDetectionStrategy.OnPush不按照我的预期工作。

8 浏览
0 Comments

ChangeDetectionStrategy.OnPush不按照我的预期工作。

我正在尝试熟悉Angular 2的ChangeDetectionStrategy.OnPush性能提升(如这里所解释的)。但是我在这里遇到了一个奇怪的情况。

我有一个父组件AppComponent:

@Component({
  selector: 'my-app',
  template: `

` }) 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只反映引用的变化,但在一个示例中,我尝试更改(突变)对象的属性,而Angular仍然会反映出来。

`this.title.name = "changed";` 不应该被检测到(因此UI不应该反映这个变化)。

plunker上有这个案例。

为什么会这样?怎么做才对?

0
0 Comments

ChangeDetectionStrategy.OnPush doesn't act how I expect it to的问题出现的原因是,在AppComponent的ngOnInit方法中,对title对象的name属性进行了修改。解决方法是,只修改this.title.name的值,而不修改对象本身,即只使用this.title.name = "changed"语句,不使用this.dTitle = {name: "changed"}语句。

首先,需要理解当在模板中使用{{title.name}}时,Angular会查找当前组件实例中的title对象,并从中获取name属性的值,并在DOM中反映出来。但在AppComponent中的配置中,title对象在两个组件中是相同的(指向同一个内存位置)。

当Angular运行LiteralsComponent组件的变更检测时,它访问的是你在AppComponent中对title对象进行修改的同一个对象。但是有趣的观察是,这个变化在OnPush和没有OnPush的情况下都没有被检测到。

最后需要理解的是DOM何时更新。根据这篇文章,DOM在当前组件的变更检测期间更新。这意味着如果当前组件没有被检查,DOM就不会被更新。所以我们为LiteralsComponent指定了ChangeDetectionStrategy.OnPush,这样视图就不会被更新。

但是,在你的问题中,视图却被更新了。这就是dTitle的作用。通过这个属性,你实际上修改了引用,Angular检测到绑定的变化,并运行LiteralsComponent组件的变更检测。当进行变更检测时,DOM也会更新。所以Angular也会更新{{title.name}},因为它指向AppComponent中的同一个对象,尽管它没有检测到它已经被修改。

0