角度性能:随机输入的每次点击都会重新计算ngStyle [英] Angular performance: ngStyle recalculates on each click on random input
问题描述
我有一个非常愚蠢的性能问题.
I have a very stupid performance issue.
我有一个使用ngStyle
的组件,我希望不重写它.但是,每次我在同一页面上点击随机input
(甚至从另一个组件中)时,ngStyle
都会重新计算(并且执行起来很慢).
I have a component that uses ngStyle
, and I'd prefer to not rewrite it. But each time I click random input
on the same page (even from another component), ngStyle
recalculates (and do it pretty slow).
比方说,我想有一张带有动态背景的乘法表:
Let's say I want to have a table of multiply with dynamic background:
<section>
<div class="row"
*ngFor="let row of rows">
<div class="col"
[ngStyle]="{'background-color': getBG(row*col)}"
*ngFor="let col of cols ">
{{row * col}}
</div>
</div>
</section>
然后在同一页面上,由于某种原因,我想添加几个输入:
Then on the same page I want to add several inputs for a some reason:
<section>
<input type="text" [ngModel]="model1"/>
<input type="text"[ngModel]="model2"/>
<input type="text"[ngModel]="model3"/>
<input type="text"[ngModel]="model4"/>
<input type="text"[ngModel]="model5"/>
</section>
现在,每次我单击这些输入之一时-getBG()
将被调用.即使该函数只返回一个字符串而无需进行任何计算,它仍然非常慢
Now each time I click on one of those inputs - getBG()
will be called. And even if that function just return a string without any calculations - it's still super slow
StackBlitz中的示例-只需打开提示框并尝试在其中快速单击即可不同的输入字段,或输入一个值.即使作为用户,我也可以看到它完全没有响应
Example at StackBlitz - just open consomle and try to click swiftly among different input fields, or enter a value. Even as a user I can see it's not responsive at all
UPD1 :我的实际案例要复杂得多.并且已经使用ChangeDetectionStrategy.OnPush
.将ngStyle
绑定到值而不是函数也无济于事-更快但仍然很慢(并且产生很多复杂性).我想要的是,这可能是一种告诉ngStyle
在我明确询问之前不要重新计算的方法.也许ChangeDetectorRef.detach()
可以帮助
UPD1: My real-world case much more complex. And already use ChangeDetectionStrategy.OnPush
. Binding ngStyle
to a value instead of function also doesn't help much - it faster but still slow (and produces a lot of complexity). What I want, it's probably a way to tell ngStyle
to not recalculate until I'll ask explicitly. Maybe ChangeDetectorRef.detach()
could help
推荐答案
这很合理.这就是Angular执行更改检测的方式.这是Angular执行的额外检查,因为您是使用以下一种数据绑定语法调用函数的:
That makes perfect sense. This is how Angular performs change detection. And this is Angular performing extra checks since you called a function in one of the data-binding syntaxes, here:
[ngStyle]="{'background-color': getBG(row*col)}"
Angular在三种情况下执行更改检测:
Angular performs Change Detection in three cases:
- DOM事件.
- AJAX呼叫.
- 超时/间隔.
这是DOM事件(click
)的情况.
This is the case of DOM Events (click
).
现在执行更改检测时,Angular检查组件中的特定变量是否已更改.
Now when performing Change Detection, Angular check whether a particular variable in the Component has changed.
对于属性,这很简单.但是对于函数来说却不是那么简单.
That's pretty straight forward in case of properties. But not so straight-forward in case of functions.
您知道,确定函数值是否已更改的唯一方法是调用它.
You see, the only way to determine whether the value of a function has changed is by calling it.
所以Angular就是这样做的.
So Angular is doing just that.
只需在组件类中为要显示的数字和要绘制的颜色创建一个矩阵:
Just create a matrix for the number to show and the color to paint right in the Component Class:
import { Component } from '@angular/core';
@Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
rows = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
cols = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
matrix = [];
model1 = '';
model2 = '';
model3 = '';
model4 = '';
model5 = '';
ngOnInit() {
this.rows.forEach((row, rowIndex) => {
this.matrix.push([]);
this.cols.forEach((col, colIndex) => {
const product = row * col;
this.matrix[row].push({
numberToShow: product,
color: this.getBG(product),
});
})
});
}
getBG(hue: number): string {
console.log('getBG was called');
return 'hsl(' + hue + ', 100%, 50%)';
}
}
然后在您的模板中使用它:
And then use it in your template:
<br/>
<div> 1. Open a console</div>
<br/>
<section>
<div class="row" *ngFor="let row of matrix">
<div
class="col"
[style.background-color]="col.color"
*ngFor="let col of row ">
{{col.numberToShow}}
</div>
</div>
</section>
<br/>
<div>2. Click fast on the different inputs: </div>
<br/>
<section>
<input type="text" [ngModel]="model1"/>
<input type="text"[ngModel]="model2"/>
<input type="text"[ngModel]="model3"/>
<input type="text"[ngModel]="model4"/>
<input type="text"[ngModel]="model5"/>
</section>
性能差异:
在先前的实现中,getBG
在初始化时被称为 401 次.
Difference in the performance:
In the previous implementation, the getBG
was called 401 times on initialization.
在解决方案实现中,getBG
在初始化时被称为 101 次.
In the solution implementation, the getBG
is called 101 times on initialization.
那是大约 397%的巨大性能提升.
That's a massive performance gain of around 397%.
此外,当用户聚焦并从任何输入字段中模糊掉时,无需再调用getBG
方法.
Plus there's no extra call to the getBG
method when the user focuses and blurs out from any input field.
这是 工作样本StackBlitz 供您参考.
Here's a Working Sample StackBlitz for your ref.
You might also want to read through a Medium Article that I wrote about Performant Reactive Forms in Angular. Although it's related to Reactive Forms, I've touched upon this aspect, in the article. I'm sure you'll find it helpful.
这篇关于角度性能:随机输入的每次点击都会重新计算ngStyle的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!