角度性能:随机输入的每次点击都会重新计算ngStyle [英] Angular performance: ngStyle recalculates on each click on random input

查看:92
本文介绍了角度性能:随机输入的每次点击都会重新计算ngStyle的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个非常愚蠢的性能问题.

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:

  1. DOM事件.
  2. AJAX呼叫.
  3. 超时/间隔.

这是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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆