ChangeDetectionStrategy.OnPush并没有按照我期望的那样运行 [英] ChangeDetectionStrategy.OnPush doesn't act how I expect it to

查看:63
本文介绍了ChangeDetectionStrategy.OnPush并没有按照我期望的那样运行的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试熟悉angular 2的 ChangeDetectionStrategy.OnPush 性能提升(如

I'm trying to get familliar with angular 2's ChangeDetectionStrategy.OnPush performance boost (as explained here). But I have curios case 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 的设置策略设置为使角度仅反映引用的变化,但是在示例中,我尝试更改(变异)对象的属性,而角度仍然反映了该属性.

I thought setting strategy to OnPush made angular only reflect changes of references but in a sample I tried changing (mutate) a property of an object and angular still reflects it.

this.title.name =已更改"; (因此,用户界面不应反映更改).

this.title.name = "changed"; shouldn't be detected (thus the UI shouldn't not reflect the change).

plunker

怎么了?怎么做对呢?

推荐答案

如果我正确理解,您会问为什么即使未修改引用, LiteralsComponent 模板中的绑定值也会更新 title ,而是对对象进行突变.

If I understand correctly you're asking why the bindings value is updated in LiteralsComponent template even if you don't modify the reference title, but rather mutate the object.

简短的答案是因为您同时修改了这两项:

The short answer is that because you modify both:

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

AppComponent.ngOnInit 中的

.如果仅修改 this.title.name ="changed" ,您将看到模板未更新.

in the AppComponent.ngOnInit. If you modify only this.title.name = "changed" you will see that template is not updated.

但是,这是一个非常有趣的问题,需要详细研究

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

Let's first start with only this.title without this.dTitle.
The first thing to understand is that when you specify the following in the template:

{{title.name}}

这是Angular的工作.它尝试在当前组件实例上找到 title 对象,然后从中获取 name 属性并将其反映在DOM中.但是具有以下配置:

here is what Angular does. It tries to find title object on current component instance, and then gets name property from it and reflects it in the DOM. But with the following configuration:

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

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

class LiteralsComponent {
     @Input() title;
}

title 对象在两个组件中均相同(指向相同的存储位置).

the title object is the same in both components (point to the same memory location).

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

So when Angular runs change detection for the LiteralsComponent component, it accesses the same object that you're changing here in AppComponent:

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

这里有趣的观察是,使用 OnPush 或不使用 OnPush 都根本不会检测到改变:

The interesting observation here is the change isn't detected at all neither with OnPush nor without it:

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更新的时间.根据这篇文章,它在CD中针对当前组件进行了更新.这意味着如果不检查当前组件,则不会更新DOM.因此,我们为 LiteralsComponent 指定 onPush :

Now, the last thing is to understand is when the DOM is updated. According to this article, it's updated during CD for the current component. It means that if the current component isn't checked, the DOM won't be updated. So we specify onPush for the LiteralsComponent:

changeDetection: ChangeDetectionStrategy.OnPush,

视图不会更新.

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

这就是 dTitle 发挥作用的地方.使用此属性,您实际上是在修改参考,并且Angular 检测绑定更改并为 LiteralsComponent 组件运行CD.我们从上面了解到,运行CD时,将更新DOM.因此,Angular也会更新 {{title.name}} ,因为它指向 AppComponent 中的同一对象,尽管它没有检测到它已更改>.

And this where dTitle comes into play. With this property, you're actually modifying the reference and Angular detects bindings changes and runs CD for the LiteralsComponent component. And we learnt above that when CD is run, the DOM is updated. So Angular also updates {{title.name}} since it points to the same object in AppComponent, although it didn't detect it was changed.

这篇关于ChangeDetectionStrategy.OnPush并没有按照我期望的那样运行的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆