Angular2 模板表达式在 change-detection 上为每个组件调用两次

Angular2 template expression called twice for each component on change-detection

相当标准的情况。

有一个 parent 个组件 <item-list>。 在其带有 *ngFor 的模板中生成了 20 child 个组件 <item-block>。 Child 使用 [ngStyle] 指令和调用函数 setStyles().

的模板表达式设置的组件样式

问题(或可能不是)是当任何事件在一个特定的 child 元素上发出时,表达式 setStyles() 对每个 child 组件执行两次。

因此,如果我们在示例中单击一个特定项目,并且我们有 20 个 <item-block> 组件 - setStyles() 将执行 20+20 次。

问题是

  1. 为什么会这样,这是预期的行为。
  2. 它如何影响性能
  3. 如何避免 - 每个 child component/detection 更改只调用一次。

例子&plnkr:

plnkr (click on item - open console for debug output)

import {Component} from '@angular/core'

@Component({
  selector: 'item-list',
  template: `
    <item-block
        [item]="item"
        *ngFor="let item of items"
    ></item-block>
  `,
})
export class ItemListComponent {

  items: any[] = [];

  constructor() {}

  ngOnInit() {
     // generate dummy empty items
    for (let i = 0; i < 20; i++) {
      this.items.push(
        {
          value: 'item #' + i; 
        }
      )
    }
  }
}

import {Component, Input} from '@angular/core'

@Component({
  selector: 'item-block',
  template: `
    <div
      class="item"
      [ngStyle]="setStyles()"
      (click)="testClick($event)"
    >{{item.value}}</div>
  `,
})
export class ItemBlockComponent {

  @Input() item: any;

  constructor() {}

  testClick(): void{
      console.log('item clicked');
  }

  setStyles(){
      console.log('seting styles...');
      return {
          'background': '#ccc'
      };
  }
}
[ngStyle]="setStyles()"

导致每次更改检测 运行 时调用 setStyles(这可能很频繁并且会损害性能)。也因为每次 setStyles() returns 一个不同的对象实例,它应该会导致异常。 "Expression changed since it was last checked" 或类似的。

不鼓励以这种方式从视图调用方法。

而是将值分配给 属性 并绑定到 属性:

[ngStyle]="myStyles"

默认(开发模式)Angular 运行检测变更机制两次.
生产模式中减少到单一变化

如何将 Detect Changes 机制切换到生产环境?

main.ts 文件中尝试添加:

import { enableProdMode } from '@angular/core';
// ...
enableProdMode();
// ...
platformBrowserDynamic().bootstrapModule(AppModule)

并重新加载应用程序。