You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.03 11:31:05