Angular2模板表达式在更改检测时为每个组件调用两次

时间:2017-01-04 18:53:19

标签: angular angular2-template angular2-components angular2-changedetection

非常标准的情况。

有一个父组件<item-list>。 其*ngFor模板内部生成了20个子组件<item-block>。 使用[ngStyle]指令和模板表达式设置的子组件样式调用函数setStyles()

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

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

问题

  1. 为什么会发生这种情况并且是预期的行为。
  2. 它如何影响表现
  3. 如何避免 - 每个子组件/检测更改只能调用一次。
  4. 示例&amp; 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'
          };
      }
    }
    

2 个答案:

答案 0 :(得分:3)

[ngStyle]="setStyles()"

每次运行更改检测时都会调用setStyles(这可能经常会影响性能)。此外,因为setStyles()每次都返回一个不同的对象实例,它应该导致异常。 “自上次检查后表情发生变化”或类似情况。

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

而是将值赋给属性并绑定到该属性:

[ngStyle]="myStyles"

答案 1 :(得分:0)

默认情况下(开发模式)角度运行检测更改机制两次
生产模式中,它会减少为单次更改

如何将检测更改机制切换为生产?

main.ts文件中尝试添加:

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

并重新加载应用程序。