ChangeDetectionStrategy.OnPush不按照我的预期工作。
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上有这个案例。
为什么会这样?怎么做才对?
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中的同一个对象,尽管它没有检测到它已经被修改。