angular 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()
テストの目的で、これらのメソッドごとに新しいバージョンを作成しました。今回はプロミスを使用します:
getCategoriesP()getSubcategoriesP(uuid)getProductsP(uuid)
呼び出されたメソッドのバージョンに関係なく、同じ問題が発生します。
アプリケーションをSPA(シングルページアプリケーション)として実行している場合、これは時間の経過に伴うパフォーマンス低下の原因の1つである可能性があります。
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
これがパフォーマンスの問題を解決するかどうかはわかりませんが、正しい方向への一歩になる可能性があります。
ルートパラメーターが変更されるたびに新しいサブスクリプションを作成しているため、多くのサブスクリプションが発生する可能性があります。
_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); } ); });
問題は、get-Methodsのサブスクリプションメカニズム内のどこかにあると思います
(getProducts、getCategoriesなど)
Api-Serviceの呼び出しから返されるオブザーバブルをどのように作成しますか? api-Serviceを呼び出した後、その呼び出しの戻り値をサブスクライブします。それはhttp-requestからの元の応答ですか?それとも、あなたが自分で作成したことは可測ですか?
一般的に、ここで説明するように、http-callsのアンサブスクライブを角度で呼び出す必要はありません。
メモリリークを防ぐためにAngular 2つのhttp呼び出しから退会する必要がありますか?
ただし、元のhttp-observableを通過せず、代わりに独自のabservableを作成する場合は、自分でクリーンアップする必要があります。
たぶん、あなたのAPIサービスのいくつかのコードを投稿できますか?これらの約束をどのように作成しますか?
別のこと:毎回異なるuuidでgetProducts-Methodを呼び出していますか?そのメソッドを呼び出すすべてのuuidを使用して、localStorageに新しいエントリを書き込みます。