angular websocket前后帧数据对比,性能优化
上篇链接: angular中websocket服务消息订阅
主要解决websocket数据量比较大,且需要一次性展示时,导致页面卡顿问题
实现思路为
- 记录
当前websocket返回数据
与上一次websocket数据
,已经最终处理的数据
,- 将两次数据进行比对,拿到
相同的数据
,- 再将相同的数据对比上一次数据,找出
减少的数据
,- 将最新数据对比相同数据,找出
新增的数据
,- 如果是新增的就直接将它
push到最终处理的数据
中,- 然后通过
lodash
的pullAllWith()
方法,将减少的数据
从最终数据中移除
,- 最后用相同的数据的值去更新最终数据的值。避免每次都将整个数据重新绘制。
第一步:引入ChangeDetectorRef模块
import { ChangeDetectorRef} from '@angular/core';
第二步:声明ChangeDetectorRef模块
constructor(private cd: ChangeDetectorRef){}
第三步:引用
在数据修改后调用,从而实现实时检测页面及其子元素的变化(根据我的实际情况)
this.cd.detectChanges();
引入lodash插件
import _ from 'lodash';
————————————————————————————————————————————————————————————————————————————————
// currentFrameData websocket每次最新数据
// this.preFrameData 记录上一次数据
// this.lineOfTrains 处理后展示数据
// 为了提高svg的性能,svg不需要每一帧数据都重新销毁列车然后创建,而是只需更新相同的, 删除减少的,添加新增的。
comparePreFrameDataWithCurrentFrameData(currentFrameData: SvgTrain[]) {
// 前后相同的
const sameTrains = _.intersectionWith(
currentFrameData,
this.preFrameData,
(value: SvgTrain, othValue: SvgTrain) => {
return value.trainNo === othValue.trainNo;
},
);
// 减少的
const deleteTrains = _.xorWith(sameTrains, this.preFrameData, (value: SvgTrain, othValue: SvgTrain) => {
return value.trainNo === othValue.trainNo;
});
// 新增的
const addTrains = _.xorWith(currentFrameData, sameTrains, (value: SvgTrain, othValue: SvgTrain) => {
return value.trainNo === othValue.trainNo;
});
this.addTrains(addTrains);
this.removeTrains(deleteTrains);
this.updateTrains(sameTrains);
// 保存当前帧数据
this.preFrameData = this.lineOfTrains;
this.cdr.markForCheck(); // 在数据修改后调用,从而实现实时检测页面及其子元素的变化
}
// 添加列车
addTrains(trains: SvgTrain[]) {
this.lineOfTrains.push(...trains);
}
// 删除列车
removeTrains(trains: SvgTrain[]) {
_.pullAllWith(this.lineOfTrains, trains, (value: SvgTrain, othValue: SvgTrain) => {
return value.trainNo === othValue.trainNo;
});
}
// 更新列车
updateTrains(trains: SvgTrain[]) {
// 用当前帧数据更新上一帧数据。
this.updateTrains(this.lineOfTrains, trains);
// 更新列车详情弹框位置。
// this.updatePopoverLocation();
}
/**
* 将新数组中的对象属性更新至老数组中的对象,目的是为了减少svg的创建和销毁动作,提高性能。
*/
updateTrains(orgTrains: SvgTrain[], newTrains: SvgTrain[]) {
newTrains.forEach((newTrain: SvgTrain) => {
const orgTrain: SvgTrain = _.find(orgTrains, (item: SvgTrain) => {
return item.trainNo === newTrain.trainNo;
});
// 这5个属性决定了列车在svg上的位置和列车颜色,所以其中任意一个不相等,都重新开始计算x,y值和列车颜色
if (
orgTrain.nextstation !== newTrain.nextstation ||
orgTrain.stopStationId !== newTrain.stopStationId ||
orgTrain.currentstation !== newTrain.currentstation
) {
// 清除缓存,触发重新计算
orgTrain.clearCache();
}
_.forEach(newTrain, (value, key) => {
orgTrain[key] = value;
});
});
}
export class SvgTrain {
lineNo: string; // 所属线路号
trainType: string; // 车型
trainNo: string; // 车号
healthStatus: string; // 健康状态
currentstation: number; // 当前站id
trainstate: SvgTrainState; // 车辆模式
whstate: string; // 运营状态 '1' 在途, '2'入库不稳定, '3'入库稳定, '-1'没有车的位置
stopStationId: number; // 停靠的站点
nextstation: number; // 下一站id
currentstation: number; // 当前站id
private _x: number;
private _y: number;
private _preStationName: string; // 上一站的站名
private _currentStationName: string; // 当前站的站名
private _nextStationName: string; // 下一站的站名
private _trainColor: string; // 列车图标颜色
// 用于清除已缓存的数据
clearCache() {
this._x = undefined;
this._y = undefined;
this._preStationName = undefined;
this._currentStationName = undefined;
this._nextStationName = undefined;
this._trainColor = undefined;
}
}
Angular *ngFor中使用trackBy提升性能
如果集合
lineOfTrains
数据发生改变,angular不知道如何跟踪集合里面的每一项,就删除了全部的项然后再重新赋值。这样就进行了大量的DOM操作,体验很不好。我们使用TrackBy来跟踪集合里面的每一项,页面就只重绘新增和修改的项,大大提升页面性能。
HTML
<ul>
<li *ngFor="let item of lineOfTrains; trackBy:trackByItem">{{item.lineNo}}<li>
<ul>
ts
interface Item{
id:number;
lineNo: string; // 所属线路号
trainType: string; // 车型
trainNo: string; // 车号
healthStatus: string; // 健康状态
currentstation: number; // 当前站id
trainstate: SvgTrainState; // 车辆模式
whstate: string; // 运营状态 '1' 在途, '2'入库不稳定, '3'入库稳定, '-1'没有车的位置
stopStationId: number; // 停靠的站点
nextstation: number; // 下一站id
currentstation: number; // 当前站id
}
//跟踪(保证唯一性)
trackByItem = (index: number, item: Item) => item.id;