在 Angular 2 中应用频繁 CSS 更改的最有效方法
Posted
技术标签:
【中文标题】在 Angular 2 中应用频繁 CSS 更改的最有效方法【英文标题】:Most effective way to apply frequent CSS changes in Angular 2 【发布时间】:2017-10-17 23:54:38 【问题描述】:我们有一个 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.03 毫秒!
我们都有 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 毫秒”
我尝试将 setTimeout 用于重复调用:结果相同,但警告消息已修改:“[Violation] 'setTimeout' 处理程序花费了 xx 毫秒”
我什至尝试清空每一行的 checkItems,但仍然收到警告。
警告是从 zone.js 中抛出的,似乎是 Angular 内部建议。我知道我可以在 Chrome 开发人员工具中关闭详细日志记录,但我经常将 console.debug 用于开发目的,这意味着它们也会消失。
我猜对于慢速函数来说,警告是可以的,但在这种情况下,它只是触发了一个 setInterval 函数。为什么会很慢?
【问题讨论】:
相当盲目的建议:如果你时间:setInterval( () => start; this.checkItems(); end; , 1000);
会发生什么? 0.03 ms
是一致的度量,当 Chrome 记录 'setInterval' handler took 74ms
时您也会观察到?
你能在没有 @Injectable
装饰器的情况下测试你的类吗?
这不是一个通用的解决方案,但是您可以将一个类应用于一批行并使其一起淡出。它会节省很多计算
在这种情况下,如果进程由具有许多子组件的智能组件处理,您还应该使用ChangeDetectionStrategy.OnPush
Array.filter
导致此问题,尝试基本的for
循环,看看它是否持续
【参考方案1】:
你确定你在从 checkItems 清除代码后重新构建了你的项目。
据我所知,它抱怨 checkItems 函数花费的时间太长。
也以防万一, 我确实设置了StackBlitz here
使用您的代码并且无法重新创建它..
如果您仍然遇到问题,也许您可以分叉 StackBlitz 并尝试在那里重现问题?
【讨论】:
【参考方案2】:我会放弃 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)
【讨论】:
【参考方案3】:据我了解,您希望 css 更改在一定间隔后消失。
-
具有正常的 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)
希望这会有所帮助。
【讨论】:
【参考方案4】:一种解决方案是使用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
,然后在5 秒内转换为opacity: 1
。
animate()
的第一个参数(例如'5s'
)也可以用来定义delay and easing。您提到“新项目”样式可以应用 4 秒,因此转换规则可以是例如:
transition(':enter', [
style( backgroundColor: 'green' ),
animate('1s 4s ease-in', style( backgroundColor: 'white' )),
])
这将立即在新项目上应用绿色背景,然后在 4 秒后为它们提供白色背景颜色(过渡长度为 1 秒)。
【讨论】:
【参考方案5】:在这样的车站可以做很多事情。
第一个是将 ChangeDetectionStrategy 更改为 ChangeDetectionStrategy.onPush ,然后仅在需要时激活检测,在您的情况下,在 checkItems 末尾添加和删除.这将大大减少脚本编写,因为 Angular 仅在被询问时才需要评估您的 html
您可以做的第二件事是检查您的寺庙是否在 *ngIf 中有函数调用 或 *ngFor,如果你这样做,angular 将无法缓存此函数返回的值,并且必须为每次检查处理它们
您应该考虑的第三件事是优化 checkItems,checkItems 运行时间为 O(n^2) 并执行许多不必要的循环。你可以减少它
checkItems()
this.timerItems = this.timerItems.filter(t =>
if(t.dueTime <= now)
this.dueTimeReachedSubject.next(t.key);
return false;
return true;
);
对于小型阵列,它不会有太大帮助,但越大,此更改将开始生效..
我猜想可以做更多的事情,但这 3 件事可以帮助我解决类似的性能问题
【讨论】:
以上是关于在 Angular 2 中应用频繁 CSS 更改的最有效方法的主要内容,如果未能解决你的问题,请参考以下文章
如何在 Chrome 开发人员工具中将 CSS 样式更改保存到 ANGULAR 2.0 组件表单?
如何在 Angular 2 中动态更改 :host 中的 CSS?
使用不同的 CSS 样式表动态更改 Angular 组件的样式
Angular Material ng-tns 类更改了我表中项目的边距,可以使用 CSS 来更改边距,但随后应用了 ng-tns 的另一个变体?