我有一个角度4.3.5的应用程序,在使用一段时间后变得越来越慢(约20分钟)。
我的方案如下:
会发生什么:
其他信息:
出现问题的组件如下:
import { Component, OnInit, OnDestroy } from '@angular/core';
import { Router, ActivatedRoute } from '@angular/router';
import 'rxjs/add/operator/takeUntil';
import { Subject } from 'rxjs/Subject';
import { SweetAlertService } from 'ng2-cli-sweetalert2';
import { ApiService } from '.././api.service';
import { NFCService } from '.././nfc.service';
@Component({
selector: 'app-menu',
templateUrl: './menu.component.html',
styleUrls: ['./menu.component.scss']
})
export class MenuComponent implements OnInit, OnDestroy {
private ngUnsubscribe: Subject<void> = new Subject<void>();
cardId: string;
userId: string;
userName: string;
is_secure_bar: boolean = false;
requestInProgress = false;
userBalance: number = 0;
step: number = 1;
// showCheckout: boolean = false;
categories = new Array();
subcategories = new Array();
products = new Array();
cartItems = new Array();
countCartItems: number = 0;
totalCartValue: number = 0;
table_scroller;
table_scroller_height;
show_scroller_btns = false;
constructor(
public router: Router,
public route: ActivatedRoute,
private _nfcService: NFCService,
private _apiService: ApiService,
private _swal: SweetAlertService
) { }
ngOnInit() {
var account = localStorage.getItem('account');
if (account) {
// set variable to catch user data
// JSON.parse(
} else {
this.router.navigate(['login']);
}
this.route.params
.takeUntil(this.ngUnsubscribe)
.subscribe(params => {
this.cardId = params.id;
this._apiService.getCardUser(params.id)
.takeUntil(this.ngUnsubscribe)
.subscribe(
response => {
// SUCCESS
this.userId = response.data[0].uuid;
this.userBalance = response.data[0].balance;
this.userName = response.data[0].name;
},
error => {
// ERROR
console.log('Failed ;(', error);
}
);
});
this.getEvents()
.takeUntil(this.ngUnsubscribe)
.subscribe(
response => {
if (response.data[0].options.sales_auth_after_buy_is_required) {
this.is_secure_bar = true;
}
},
error => {
console.log('Erro ao verificar Evento.')
}
);
var categories = localStorage.getItem('cache_categories');
if (categories) {
this.categories = JSON.parse(categories);
} else {
// this.getCategories();
this.getCategoriesP()
}
}
//@felipe_todo
getEvents()
{
return this._apiService.getEvents();
//COMO FAZER LOGOUT ABAIXO
//localStorage.clear();
}
getCategories() {
this._apiService.getProductsCategories()
.takeUntil(this.ngUnsubscribe)
.subscribe(response => {
// SUCCESS
this.categories = response.data;
localStorage.setItem('cache_categories', JSON.stringify(this.categories));
}, error => {
// ERROR
console.log('Failed ;(', error);
});
}
getCategoriesP() {
let categories;
this._apiService.getCategories()
.then(response => categories = response)
.then(() => {
this.categories = categories;
console.log(categories);
});
}
categorySelected(item) {
this.step = 2;
var subcategories = localStorage.getItem('cache_subcategories_' + item.uuid);
if (subcategories) {
this.subcategories = JSON.parse(subcategories);
} else {
// this.getSubcategories(item.uuid);
this.getSubcategoriesP(item.uuid);
}
}
getSubcategories(uuid) {
this._apiService.getProductsSubcategories(uuid)
.takeUntil(this.ngUnsubscribe)
.subscribe(response => {
// SUCCESS
this.subcategories = response.data;
localStorage.setItem('cache_subcategories_' + uuid, JSON.stringify(this.subcategories));
}, error => {
// ERROR
console.log('Failed ;(', error);
});
}
getSubcategoriesP(uuid) {
let subcategories;
this._apiService.getSubcategories(uuid)
.then(response => subcategories = response)
.then(() => {
this.subcategories = subcategories;
console.log(subcategories);
});
}
subCategorySelected(item) {
this.step = 3;
var products = localStorage.getItem('cache_products_' + item.uuid);
if (products) {
this.products = JSON.parse(products);
} else {
// this.getProducts(item.uuid);
this.getProductsP(item.uuid);
}
}
getProducts(uuid) {
this._apiService.getProducts(uuid)
.takeUntil(this.ngUnsubscribe)
.subscribe(response => {
// SUCCESS
this.products = response.data;
localStorage.setItem('cache_products_' + uuid, JSON.stringify(this.products));
}, error => {
// ERROR
console.log('Failed ;(', error);
});
}
getProductsP(uuid) {
let products;
this._apiService.getProductList(uuid)
.then(response => products = response)
.then(() => {
this.products = products;
console.log(products);
});
}
addToCard(product) {
var existentItems = this.cartItems.filter(function(item) {
return item.uuid === product.uuid
});
if (existentItems.length) {
existentItems[0].quantity += 1
} else {
product.quantity = 1;
this.cartItems.unshift(product);
}
let that = this;
this.calculateTotal();
setTimeout(function(){
that.setScroller();
}, 300);
}
removeProduct(index) {
let product = this.cartItems[index]
var existentItems = this.cartItems.filter(function(item) {
return item.uuid === product.uuid
});
if (existentItems.length) {
existentItems[0].quantity -= 1
if (existentItems[0].quantity == 0) {
this.cartItems.splice(index, 1);
}
} else {
product.quantity = 1;
this.cartItems.splice(index, 1);
}
this.calculateTotal();
let that = this;
setTimeout(function(){
if (that.table_scroller.offsetHeight < 270) {
that.show_scroller_btns = false;
}
}, 300);
}
calculateTotal() {
this.countCartItems = 0;
this.totalCartValue = 0;
var that = this;
this.cartItems.forEach(function(item) {
that.countCartItems += item.quantity;
that.totalCartValue += item.value * item.quantity;
});
}
backStep() {
if (this.step == 2) {
this.subcategories = new Array();
} else if (this.step == 3) {
this.products = new Array();
}
this.step--;
}
setScroller() {
if (this.cartItems.length) {
if (!this.table_scroller) {
this.table_scroller = document.querySelector('#table-scroller');
}else {
console.log(this.table_scroller.offsetHeight)
if (this.table_scroller.offsetHeight >= 270) {
this.show_scroller_btns = true;
} else {
this.show_scroller_btns = false;
}
}
}
}
scrollDown() {
(<HTMLElement>this.table_scroller).scrollTop = (<HTMLElement>this.table_scroller).scrollTop+50;
}
scrollUp() {
(<HTMLElement>this.table_scroller).scrollTop = (<HTMLElement>this.table_scroller).scrollTop-50;
}
confirmDebit() {
if (this.requestInProgress) return;
if (this.userBalance < this.totalCartValue) {
this._swal.error({ title: 'Salto Insuficiente', text: 'Este cliente não possui saldo suficiente para essa operação.' });
return;
}
this.requestInProgress = true;
var order = {
card_uuid: this.cardId,
event_uuid: 'c7b5bd69-c2b5-4226-b043-ccbf91be0ba8',
products: this.cartItems
};
let is_secure_bar = this.is_secure_bar;
this._apiService.postOrder(order)
.takeUntil(this.ngUnsubscribe)
.subscribe(response => {
console.log('Success');
// this.router.navigate(['customer', this.userId]);
let that = this;
this._swal.success({
title: 'Debito Efetuado',
text: 'O débito foi efetuado com sucesso',
showCancelButton: false,
confirmButtonText: 'OK',
allowOutsideClick: false,
}).then(function(success) {
console.log("Clicked confirm");
if (is_secure_bar) {
that.logout();
} else {
that.router.navigate(['card']);
}
});
this.requestInProgress = false;
}, error => {
// ERROR
console.log('Request Failed ;(', error);
if (error.status !== 0) {
// TODO: Should display error message if available!
this._swal.error({ title: 'Erro', text: 'Ocorreu um erro inesperado ao conectar-se ao servidor de acesso.' });
} else {
this._swal.error({ title: 'Erro', text: 'Não foi possível conectar-se ao servidor de acesso. Por favor verifique sua conexão.' });
}
this.requestInProgress = false;
}
);
}
logout() {
let that = this;
localStorage.clear();
that.router.navigate(['login']);
}
clearCheckout() {
this.cartItems = new Array();
this.calculateTotal();
this.router.navigate(['card']);
}
ngOnDestroy() {
console.log('uhul')
this.ngUnsubscribe.next();
this.ngUnsubscribe.complete();
}
}
每次访问组件时呈现缓慢的方法是:
getCategories() getSubcategories(UUID) 的getProducts(UUID) confirmDebit()
出于测试目的,我们为每种方法创建了一个新版本,这次使用了promises:
getCategoriesP() getSubcategoriesP(UUID) getProductsP(UUID)
无论被调用方法的版本如何,都会出现同样的问题。
答案 0 :(得分:12)
如果您将应用程序作为SPA(单页应用程序)运行,那么这可能是随着时间的推移性能下降的原因之一。
在SPA中,每次用户访问新网页时,DOM都会变得更重。因此,您必须研究如何保持DOM轻量级。
以下是我发现显着提高应用程序性能的主要原因。
检查以下几点:
confirmation popup
和message alert
等常见组件应定义一次并可全局访问。 服务完成后,在任何对象上调用destroy:(以下是调用服务的示例)
import { Subject } from 'rxjs/Subject'
import 'rxjs/add/operator/takeUntil';
ngOnDestroy() {
this.ngUnsubscribe.next(true);
this.ngUnsubscribe.complete();
}
this.frameworkService.ExecuteDataSource().takeUntil(this.ngUnsubscribe).subscribe((data: any) => {
console.log(data);
});
请参阅以下链接了解更多详情:
https://medium.com/paramsingh-66174/catalysing-your-angular-4-app-performance-9211979075f6
答案 1 :(得分:2)
我认为问题出在get-Methods
中的订阅机制中(getProducts,getCategories等)
如何创建从api-Service调用返回的observable?在您调用api-Service后,您将订阅该呼叫的返回值。这是来自http请求的原始响应吗?或者这是你自己创造的一个oeservable?
一般情况下,你不需要在角度的http-calls上调用unsubscribe,如下所述:
Do you need to unsubscribe from Angular 2 http calls to prevent memory leak?
但是如果您没有通过原始的http-observable,而是创建自己的abservable,那么您可能需要自己清理它。
也许您可以发布一些api-Service的代码?你如何创造这些承诺?
另一件事: 你打电话给你getProducts - 每次都用不同的uuid方法?您将使用
调用该方法的每个uuid在localStorage中写入一个新条目答案 2 :(得分:2)
不确定这是否会解决您的性能问题,但这可能是朝着正确方向迈出的一步。
每次路由参数更改时,您都在创建新的订阅,因此最终可能会有大量订阅:
this.route.params
.takeUntil(this.ngUnsubscribe)
.subscribe(params => {
this.cardId = params.id;
this._apiService.getCardUser(params.id)
.takeUntil(this.ngUnsubscribe)
.subscribe(
response => {
// SUCCESS
this.userId = response.data[0].uuid;
this.userBalance = response.data[0].balance;
this.userName = response.data[0].name;
},
error => {
// ERROR
console.log('Failed ;(', error);
}
);
});
我认为你最好使用switchMap,这样只会有1个订阅。类似的东西:
this.route.params
.switchMap(params => {
this.cardId = params.id;
return this._apiService.getCardUser(params.id)
})
.takeUntil(this.ngUnsubscribe)
.subscribe(
response => {
// SUCCESS
this.userId = response.data[0].uuid;
this.userBalance = response.data[0].balance;
this.userName = response.data[0].name;
},
error => {
// ERROR
console.log('Failed ;(', error);
}
);
});