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 次。
问题是:
- 为什么会这样,这是预期的行为。
- 它如何影响性能
- 如何避免 - 每个 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)
并重新加载应用程序。
相当标准的情况。
有一个 parent 个组件 <item-list>
。
在其带有 *ngFor
的模板中生成了 20 child 个组件 <item-block>
。
Child 使用 [ngStyle]
指令和调用函数 setStyles()
.
问题(或可能不是)是当任何事件在一个特定的 child 元素上发出时,表达式 setStyles()
对每个 child 组件执行两次。
因此,如果我们在示例中单击一个特定项目,并且我们有 20 个 <item-block>
组件 - setStyles()
将执行 20+20 次。
问题是:
- 为什么会这样,这是预期的行为。
- 它如何影响性能
- 如何避免 - 每个 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)
并重新加载应用程序。