在Angular 2

时间:2017-05-18 08:59:15

标签: javascript css angular animation

我们有一个Angular 2站点,其中有一个websocket,用于从后端到我们网格的数据。为了表明最近的更新,我们使用CSS来设置行的背景颜色,并在受影响的单元格上设置粗体字。

该指示应该只持续很短的时间。

1)我们的第一次尝试是在下一批从服务器到达时重置所有指标。这样做效果很好,但在某些观点中,很少有更新,这意味着指标可以保持很长时间,这有点令人困惑。

如果更新指标在固定的时间间隔后消失,例如4秒,那就会更加一致。

2)我们的下一次尝试是使用CSS动画。但过了一段时间,它已经落后很多了。给人的印象是,运行过多的动画会使浏览器超负荷,无法处理请求的时间。也许每个动画在后台都有自己的计时器?

3)第三次尝试是让一个计时器以固定间隔运行,然后检查要重置的记录。我们创建了一个定期检查到期项目的TimerService。将项添加到计时器池时,可以配置任意等待时间。

这样可行,但在日志窗口中经常出现违规警告:

[Violation] 'setInterval' handler took 56ms
[Violation] 'setInterval' handler took 74ms
[Violation] 'setInterval' handler took 63ms
[Violation] 'setInterval' handler took 88ms
...

但是当我们计算checkItems方法内部发生的事情时,它只需要0.03ms!

  • 我们都有C#背景,并且刚刚与Angular合作了几个月。也许我们正在采用后端方法?

  • 我们错过了上下文切换吗?

  • 是否有其他更多前端友好方法?

  • 我们可以对代码进行一些重要的优化吗?

所有建议都表示赞赏!

以下是建议的TimerService导致所有警告:

import { Injectable, OnInit } from "@angular/core";
import { Observable } from "rxjs/Rx";
import { Subject } from "rxjs/Subject";

@Injectable()
export class TimerService {
    private timerItems: TimerItem[] = [];
    private dueTimeReachedSubject: Subject<string> = new Subject<string>();
    public dueTimeReached: Observable<string> = this.dueTimeReachedSubject.asObservable();

    constructor() {
        setInterval(() => this.checkItems(), 1000);
    }

    private checkItems() {
        let now = Date.now();
        let removeKeys: string[] = [];
        this.timerItems.filter(t => t.dueTime <= now).forEach(t => {
            this.dueTimeReachedSubject.next(t.key);
            removeKeys.push(t.key);
        });
        this.timerItems = this.timerItems.filter(t => removeKeys.indexOf(t.key) < 0);
    }

    public add(key: string, delayInSeconds: number) {
        let dueTime = Date.now() + delayInSeconds * 1000;
        let timerItem = this.timerItems.find(t => t.key === key);
        if (timerItem) {
            timerItem.dueTime = dueTime;
        }
        else {
            this.timerItems.push(new TimerItem(key, dueTime));
        }
    }   

    public remove(key: string) {
        this.timerItems = this.timerItems.filter(t => t.key !== key);
    }
}


class TimerItem {
    constructor(public key: string, public dueTime: number) { }
}

修改

我尝试使用Observable.interval:完全相同的警告消息结果相同:“[Violation]'setInterval'处理程序占用xx ms”

我尝试使用setTimeout重复调用:结果相同,但修改了警​​告消息:“[Violation]'setTimeout'处理程序占用了xx ms”

我甚至试图清空每一行的checkItems,但仍然会收到警告。

警告是从zone.js中抛出的,似乎是一个Angular内部推荐。我知道我可以关闭Chrome开发者工具中的详细日志记录,但我经常使用console.debug进行开发,这意味着它们也会消失。

警告是可以的我想对于慢速函数,但在这种情况下它只是触发一个setInterval函数。为什么会这么慢?

5 个答案:

答案 0 :(得分:1)

我会抛弃setInterval并仅在必要时使用setTimeout。

import { Injectable, OnInit } from "@angular/core";
import { Observable } from "rxjs/Rx";
import { Subject } from "rxjs/Subject";

let timer = null

@Injectable()
export class TimerService {
    private timerItems: TimerItem[] = [];
    private dueTimeReachedSubject: Subject<string> = new Subject<string>();
    public dueTimeReached: Observable<string> = this.dueTimeReachedSubject.asObservable();

    constructor() {
        this.checkItems();
    }

    private checkItems() {
        // clear the timeout
        clearTimeout(timer)
        let now = Date.now();
        let removeKeys: string[] = [];
        this.timerItems.filter(t => t.dueTime <= now).forEach(t => {
            this.dueTimeReachedSubject.next(t.key);
            removeKeys.push(t.key);
        });
        this.timerItems = this.timerItems.filter(t => removeKeys.indexOf(t.key) < 0);
        // only use the timer if there are timerItems
        if(this.timerItems.length) {
            timer = setTimeout(() => this.checkItems(), 1000)
        }
    }

    public add(key: string, delayInSeconds: number) {
        let dueTime = Date.now() + delayInSeconds * 1000;
        let timerItem = this.timerItems.find(t => t.key === key);
        if(timerItem) {
            timerItem.dueTime = dueTime;
        }
        else {
            this.timerItems.push(new TimerItem(key, dueTime));
        }
        // check for items and engage the timer if necessary
        this.checkItems()
    }   

    public remove(key: string) {
        this.timerItems = this.timerItems.filter(t => t.key !== key);
    }
}


class TimerItem {
    constructor(public key: string, public dueTime: number) { }
}

答案 1 :(得分:1)

据我了解,您希望CSS更改在一定时间间隔后消失。

  1. 具有用于正常的css类并更新单元格和行的状态。例如

.row-normal{
//css-styling goes here
}
.row-updates{
//css- here
}

---方法1 --- 在更新监听器上设置 在html中或通过在JavaScript代码中使用document.getElementByTagName来为视图定义onChange属性。

function onUpdate(){
  let v = this;
  v.className = "row-updated";
  setTimeOut(function(){v.className = "row-normal"}, interval_in_millis)
}

希望这会有所帮助。

答案 2 :(得分:1)

在这样的电台中可以做很多事情。

第一个方法是将 ChangeDetectionStrategy 更改为 ChangeDetectionStrategy.onPush ,然后仅在需要时(在您的情况下,在checkItems末尾)激活检测,添加和删除。这将大大减少脚本编写,因为只有在被询问时,angular才需要评估您的html

您可以做的第二件事是检查您的太阳穴是否在 * ngIf 中有函数调用 或 * ngFor ,如果您执行angular操作,则将无法缓存此函数返回的值,并且必须为每次检查进行处理

您应该考虑的第三件事是优化checkItems,在O(n ^ 2)中运行checkItems并执行许多不必要的循环。你可以减少它

checkItems() {
  this.timerItems = this.timerItems.filter(t => {
      if(t.dueTime <= now){
          this.dueTimeReachedSubject.next(t.key);
          return false;
      }
      return true;
  });        
}

对于小数组,它不会有太大帮助,但越大的更改将开始生效。

我可以做更多的事情,但这三件事可以帮助我解决类似的性能问题

答案 3 :(得分:0)

确定从checkItems清除代码后,您确定要重新构建项目。

据我所知,它抱怨checkItems函数花费的时间太长。

以防万一, 我确实设置了StackBlitz here

使用您的代码,但无法重新创建它。.

如果您仍然遇到问题,也许您可​​以派出StackBlitz并尝试在那里重新创建问题?

答案 4 :(得分:0)

一种解决方案是使用Angular's animations

例如,添加到列表中的元素是:enter transition(这是void => *过渡的别名)的情况。从链接的文档中:

HTML:

<div @myInsertRemoveTrigger *ngIf="isShown" class="insert-remove-container">
  <p>The box is inserted</p>
</div>

TS:

trigger('myInsertRemoveTrigger', [
  transition(':enter', [
    style({ opacity: 0 }),
    animate('5s', style({ opacity: 1 })),
  ]),
  transition(':leave', [
    animate('5s', style({ opacity: 0 }))
  ])
]),

当带有myInsertRemoveTrigger触发器的元素将“从空白中出来”(:enter)时,它将最初获得样式opacity: 0,然后过渡到opacity: 1 5秒钟。 / p>

animate()的第一个参数(例如'5s')也可以用来定义delay and easing。您提到“新项目”样式可以应用4秒钟,因此过渡规则可以例如:

  transition(':enter', [
    style({ backgroundColor: 'green' }),
    animate('1s 4s ease-in', style({ backgroundColor: 'white' })),
  ])

这将立即将绿色背景应用于新项目,然后在4秒钟后为它们提供白色背景色(过渡时间为1秒)。