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

RxJS实现:检查localStorage缓存并静默从API拉取数据

解决方案:基于 LocalStorage 替换 shareReplay 实现缓存逻辑

核心思路

针对 Lambda 冷启动慢的问题,我们通过先读缓存、静默更新的方式优化:有缓存时立即返回缓存数据,同时后台静默拉取最新数据更新缓存;无缓存时正常发起 API 请求并缓存结果。以下是具体改造步骤:


1. 添加缓存工具函数(在 DropdownService 中)

先封装 LocalStorage 的读取/写入逻辑,包含异常处理避免解析或存储失败导致的问题:

// 缓存键名,统一管理
private readonly SITE_DDL_CACHE_KEY = 'siteDropdownCache';

// 从 LocalStorage 读取缓存并包装为 HttpRequestState 格式
private getSiteDdlCache(): HttpRequestState<CommonWebResponse<Dropdown[]>> | null {
  const cachedStr = localStorage.getItem(this.SITE_DDL_CACHE_KEY);
  if (!cachedStr) return null;
  
  try {
    const cachedData = JSON.parse(cachedStr) as CommonWebResponse<Dropdown[]>;
    // 包装为成功状态,无加载/错误标识
    return {
      isLoading: false,
      error: null,
      value: cachedData
    };
  } catch (e) {
    this.logger.error('解析站点下拉缓存失败', e);
    localStorage.removeItem(this.SITE_DDL_CACHE_KEY);
    return null;
  }
}

// 将 API 返回结果存入 LocalStorage
private saveSiteDdlCache(data: CommonWebResponse<Dropdown[]>): void {
  try {
    localStorage.setItem(this.SITE_DDL_CACHE_KEY, JSON.stringify(data));
  } catch (e) {
    this.logger.error('保存站点下拉缓存失败', e);
  }
}

2. 改造 siteDdlState$ 流逻辑

替换原有的 shareReplay(1),实现缓存优先+静默更新:

readonly siteDdlState$: Observable<HttpRequestState<CommonWebResponse<Dropdown[]>>> = this._refreshSiteDdl$.pipe(
  switchMap(() => {
    const cachedState = this.getSiteDdlCache();
    
    if (cachedState) {
      // 有缓存:先返回缓存,同时静默发起 API 请求更新缓存
      const silentApiRequest$ = this._http.get<CommonWebResponse<Dropdown[]>>(API_PATHS.siteDropdown()).pipe(
        httpRequestStates(),
        tap((state) => {
          // 静默请求不触发 toast,仅在成功时更新缓存
          if (!state.isLoading && !state.error && state.value) {
            this.saveSiteDdlCache(state.value);
          }
          // 请求失败仅打日志,不影响当前缓存展示
          if (state.error) {
            this.logger.error('静默刷新站点下拉失败', state.error);
          }
        }),
        // 过滤加载状态,避免静默请求触发页面加载动画
        filter(state => !state.isLoading),
        // 仅传递成功状态,错误状态不流入主流程
        filter(state => !state.error)
      );

      // 先发出缓存数据,再发出最新 API 数据(如果请求成功)
      return concat(of(cachedState), silentApiRequest$);
    } else {
      // 无缓存:走原有逻辑,正常显示加载状态和 toast,成功后缓存结果
      return this._http.get<CommonWebResponse<Dropdown[]>>(API_PATHS.siteDropdown()).pipe(
        httpRequestStates(),
        tap((state) => {
          this._toast.onStateChange(state, true);
          if (!state.isLoading && !state.error && state.value) {
            this.saveSiteDdlCache(state.value);
          }
        })
      );
    }
  }),
  // 保留 shareReplay(1),确保新订阅者能拿到最新状态(缓存或最新 API 数据)
  shareReplay(1)
);

3. 简化路由解析器(可选)

原解析器的 ReplaySubject 属于冗余逻辑,直接复用 siteDdlState$ 即可:

export class SiteDdlResolver implements Resolve<HttpRequestState<CommonWebResponse<Dropdown[]>>> {
  constructor(
    private dropdownService: DropdownService,
    private logger: LogService
  ) {}

  resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<HttpRequestState<CommonWebResponse<Dropdown[]>>> {
    return this.dropdownService.siteDdlState$.pipe(
      // 等待到非加载状态(无缓存时等待 API 请求完成)
      filter(state => !state.isLoading),
      first(),
      catchError(error => {
        this.logger.error('解析站点下拉失败', error);
        // 返回错误状态,或根据业务需求跳转错误页
        return of({ isLoading: false, error, value: null });
      })
    );
  }
}

4. 可选:添加缓存过期逻辑

如果需要设置缓存有效期(比如1小时),可以修改缓存工具函数:

// 保存缓存时添加时间戳
private saveSiteDdlCache(data: CommonWebResponse<Dropdown[]>): void {
  try {
    localStorage.setItem(this.SITE_DDL_CACHE_KEY, JSON.stringify({
      data,
      timestamp: Date.now()
    }));
  } catch (e) {
    this.logger.error('保存站点下拉缓存失败', e);
  }
}

// 读取时检查是否过期(1小时有效期)
private getSiteDdlCache(): HttpRequestState<CommonWebResponse<Dropdown[]>> | null {
  const cachedStr = localStorage.getItem(this.SITE_DDL_CACHE_KEY);
  if (!cachedStr) return null;
  
  try {
    const cachedObj = JSON.parse(cachedStr);
    const ONE_HOUR = 60 * 60 * 1000;
    // 缓存过期则删除并返回 null
    if (Date.now() - cachedObj.timestamp > ONE_HOUR) {
      localStorage.removeItem(this.SITE_DDL_CACHE_KEY);
      return null;
    }
    return {
      isLoading: false,
      error: null,
      value: cachedObj.data
    };
  } catch (e) {
    this.logger.error('解析站点下拉缓存失败', e);
    localStorage.removeItem(this.SITE_DDL_CACHE_KEY);
    return null;
  }
}

模板部分无需修改

原 HTML 模板的状态判断逻辑完全兼容改造后的流,直接复用即可。

内容的提问来源于stack exchange,提问作者Cody Pritchard

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.01 23:30:45