ChangeDetectionStrategy.OnPush并没有按照我期望的那样运行 [英] ChangeDetectionStrategy.OnPush doesn't act how I expect it to
问题描述
我正在尝试熟悉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).
怎么了?怎么做对呢?
推荐答案
如果我正确理解,您会问为什么即使未修改引用, 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屋!