RxJS实现:检查localStorage缓存并静默从API拉取数据
核心思路
针对 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
相关产品推荐
相关产品推荐

