Angular 10下如何通过NgRx/store减少重复API调用?
用NgRx/store减少重复API调用的实现方案
完全可以通过NgRx/store解决这个问题,它的集中式状态管理机制正好能帮你缓存API请求结果,避免相同参数的/app-config请求被重复发起。针对你这种多组件、多参数的场景,具体实现步骤如下:
1. 定义状态结构
首先在store里设计保存配置数据的状态,用键值对跟踪每个请求参数对应的数据、加载状态、错误信息:
export interface AppConfigState { data: Record<string, any>; // key为请求参数标识,value为接口返回数据 loading: Record<string, boolean>; // 跟踪每个参数请求的加载状态 error: Record<string, string | null>; // 存储每个参数请求的错误信息 } const initialState: AppConfigState = { data: {}, loading: {}, error: {} };
2. 创建Action
定义触发请求、请求成功、请求失败的Action,用来传递参数和数据:
import { createAction, props } from '@ngrx/store'; // 触发配置加载的Action export const loadAppConfig = createAction( '[AppConfig] Load Config', props<{ params: string }>() ); // 加载成功的Action export const loadAppConfigSuccess = createAction( '[AppConfig] Load Config Success', props<{ params: string; data: any }>() ); // 加载失败的Action export const loadAppConfigFailure = createAction( '[AppConfig] Load Config Failure', props<{ params: string; error: string }>() );
3. 编写Effect处理API请求
Effect是NgRx处理异步操作的核心,这里我们在Effect里做缓存校验:收到loadAppConfig Action时,先检查store里有没有对应参数的数据,或者是否正在加载——如果已有数据或正在加载,就跳过请求;否则才发起API调用:
import { Injectable } from '@angular/core'; import { Actions, createEffect, ofType } from '@ngrx/effects'; import { Store } from '@ngrx/store'; import { of } from 'rxjs'; import { mergeMap, withLatestFrom, filter, map, catchError } from 'rxjs/operators'; import { AppConfigService } from './app-config.service'; import * as AppConfigActions from './app-config.actions'; import { selectAppConfigState } from './app-config.selectors'; import { AppState } from '../app.state'; @Injectable() export class AppConfigEffects { loadAppConfig$ = createEffect(() => this.actions$.pipe( ofType(AppConfigActions.loadAppConfig), // 同步获取当前store状态 withLatestFrom(this.store.select(selectAppConfigState)), // 过滤已有数据或正在加载的请求 filter(([action, state]) => !state.data[action.params] && !state.loading[action.params]), // 发起API请求 mergeMap(([action]) => this.appConfigService.getConfig(action.params).pipe( map(data => AppConfigActions.loadAppConfigSuccess({ params: action.params, data })), catchError(error => of(AppConfigActions.loadAppConfigFailure({ params: action.params, error: error.message }))) ) ) ) ); constructor( private actions$: Actions, private store: Store<AppState>, private appConfigService: AppConfigService ) {} }
4. 编写Reducer更新状态
Reducer负责根据Action更新store状态,处理加载、成功、失败三种场景:
import { createReducer, on } from '@ngrx/store'; import * as AppConfigActions from './app-config.actions'; import { AppConfigState, initialState } from './app-config.state'; export const appConfigReducer = createReducer( initialState, // 触发加载时,标记该参数请求为加载中 on(AppConfigActions.loadAppConfig, (state, { params }) => ({ ...state, loading: { ...state.loading, [params]: true }, error: { ...state.error, [params]: null } })), // 加载成功时,存储数据并取消加载状态 on(AppConfigActions.loadAppConfigSuccess, (state, { params, data }) => ({ ...state, data: { ...state.data, [params]: data }, loading: { ...state.loading, [params]: false } })), // 加载失败时,存储错误信息并取消加载状态 on(AppConfigActions.loadAppConfigFailure, (state, { params, error }) => ({ ...state, loading: { ...state.loading, [params]: false }, error: { ...state.error, [params]: error } })) );
5. 组件中使用Store替代直接API调用
组件不再直接调用API服务,而是通过dispatch Action触发请求,然后从store中订阅对应参数的数据:
import { Component, OnInit } from '@angular/core'; import { Store } from '@ngrx/store'; import { Observable } from 'rxjs'; import * as AppConfigActions from './store/app-config.actions'; import { selectAppConfigData } from './store/app-config.selectors'; import { AppState } from './store/app.state'; @Component({ selector: 'app-some-component', templateUrl: './some-component.component.html' }) export class SomeComponent implements OnInit { configData$: Observable<any>; private targetParams = 'your-specific-param'; // 替换为实际请求参数 constructor(private store: Store<AppState>) {} ngOnInit() { // 触发配置加载 this.store.dispatch(AppConfigActions.loadAppConfig({ params: this.targetParams })); // 从store中获取对应参数的配置数据 this.configData$ = this.store.select(selectAppConfigData, { params: this.targetParams }); } }
对应的Selector用来根据参数筛选数据:
import { createFeatureSelector, createSelector } from '@ngrx/store'; import { AppConfigState } from './app-config.state'; export const selectAppConfigState = createFeatureSelector<AppConfigState>('appConfig'); export const selectAppConfigData = createSelector( selectAppConfigState, (state, props) => state.data[props.params] );
效果说明
通过这套实现,不管多少个组件发起相同参数的/app-config请求,只会触发一次网络调用,后续请求直接从store缓存中获取数据;而不同参数的请求依然会正常发起,但每个参数只会请求一次,正好解决你当前的冗余请求问题。
内容的提问来源于stack exchange,提问作者Karam
相关产品推荐
相关产品推荐

