Angular中Observable结果链式调用查询用户积分优化咨询
Angular RxJS 场景优化方案
现有代码核心问题
- 嵌套
subscribe属于RxJS典型反模式,手动创建的订阅没有对应取消逻辑,存在内存泄漏风险 - 模板中直接调用
getCustomerPoints()方法,会在每一次变更检测周期重复执行,即使积分数据没有更新也会重复触发函数,存在不必要的性能损耗 - 手动维护中间变量
customerPointsRetrieved存储积分值,数据流断裂,容易出现状态和接口返回不一致的问题 - 重复调用
userAccount.get()获取用户信息,产生多余的状态读取开销
符合最佳实践的实现
核心思路是用RxJS操作符串接完整异步数据流,全程不做手动订阅,所有订阅生命周期交给async管道自动管理,不需要额外维护中间存储变量。
1. 组件TypeScript代码优化
import { Component, OnInit } from '@angular/core'; import { AuthService, CmsComponent, User, UserIdService } from '@spartacus/core'; import { CmsComponentData } from '@spartacus/storefront'; import { UserAccountFacade } from '@spartacus/user/account/root'; import { Observable, of } from 'rxjs'; import { catchError, filter, map, switchMap } from 'rxjs/operators'; import { PointsRetrievalService } from '../points-retrieval.service'; import { FOOTER_VALUE, HEADER_VALUE } from './component-labels'; export interface CustomerPoints { points: number; } export interface CmsSlpCustomUserPointsComponent4 extends CmsComponent { title: string; headerText: string; footerText: string; } @Component({ selector: 'app-userpoints', templateUrl: './userpoints.component.html', styleUrls: ['./userpoints.component.scss'] }) export class UserpointsComponent implements OnInit { // 只需要声明最终要渲染的积分数据流,不需要多余的user$、point$和中间存储变量 customerPoints$: Observable<number>; data$: Observable<CmsSlpCustomUserPointsComponent4> = this.component.data$; constructor( public component: CmsComponentData<CmsSlpCustomUserPointsComponent4>, private pointsRetrievalService: PointsRetrievalService, protected auth: AuthService, protected userAccount: UserAccountFacade, protected userIdService: UserIdService, ) {} ngOnInit(): void { this.customerPoints$ = this.auth.isUserLoggedIn().pipe( // 串接登录状态 -> 用户信息 -> 积分数据的完整流 switchMap(isUserLoggedIn => { if (!isUserLoggedIn) { // 未登录时返回默认积分0 return of(0); } return this.userAccount.get().pipe( // 过滤掉用户信息未加载、无customerId的无效场景 filter(user => !!user?.customerId), // 拿到customerId后发起积分查询请求 switchMap(user => this.pointsRetrievalService.getPointsForCustomerId(user.customerId)), // 提取积分字段 map(pointsRes => pointsRes.points), // 接口异常时返回默认值0,避免流中断 catchError(() => of(0)) ); }) ); } }
2. 模板代码优化
不需要嵌套两层async订阅,直接订阅最终的积分数据流即可:
<div class="box"> <ng-container *ngIf="customerPoints$ | async as points"> <p>{{ points }}</p> </ng-container> </div>
3. 服务层优化
移除服务中未使用的UserAccountFacade依赖,减少冗余注入:
import { Injectable } from '@angular/core'; import { HttpClient, HttpParams } from '@angular/common/http'; import { Observable } from 'rxjs'; import { BASE_SITE_ID, CUSTOMER_ID, ELECTRONICS_SITE, HTTP_JSON_HEADERS, REST_URL } from './userpoints/rest-connection-constants'; import { CustomerPoints } from './userpoints/customer-points-interfaces'; @Injectable({ providedIn: 'root' }) export class PointsRetrievalService{ constructor(private http: HttpClient) {} getPointsForCustomerId(userID: string): Observable<CustomerPoints> { const url = `${REST_URL}${userID}`; const params = new HttpParams().set(CUSTOMER_ID, userID).set(BASE_SITE_ID, ELECTRONICS_SITE); const options = { params: params, headers: HTTP_JSON_HEADERS } return this.http.get<CustomerPoints>(url, options); } }
为什么不推荐用getPts(): number同步方法直接获取
- Angular的HTTP请求是异步执行的,同步方法无法直接捕获异步返回的结果,要么返回初始默认值,要么就得手动维护中间变量存储结果,反而容易出现状态不一致问题
- 模板中调用同步方法时,每一次Angular变更检测都会重新执行该方法,即使数据没有更新也会重复跑逻辑,长期下来会造成明显的性能损耗
- 基于Observable+
async管道的实现,Angular会自动处理订阅触发、组件销毁时取消订阅、数据更新时触发视图更新的全流程,不需要手动管理生命周期,不会出现内存泄漏问题
内容的提问来源于stack exchange,提问作者Joel
相关产品推荐
相关产品推荐

