如何避免Angular路由重定向后Resolver重复调用接口?
解决Angular路由重定向时重复调用接口的问题
问题场景
应用需从根路径/自动重定向至对应页面,重定向前需通过initialDataResolver请求接口数据确定目标路由。但目标路由/user/organisations同样配置了该Resolver,导致:
- 用户访问
/时,先调用一次接口,重定向后再次调用 - 直接访问
/user/organisations时仅调用一次接口
已尝试无效方案:
- 带状态导航:Resolver中无法获取导航状态
- 依赖
navigationId:值不稳定,无法准确判断重定向 - 依赖路由数据:重定向后数据不可用
可行解决方案
方案一:将Resolver逻辑迁移至服务并缓存数据
把接口请求逻辑抽离到独立服务,在服务中实现数据缓存,Resolver调用服务方法时优先使用缓存,避免重复请求。
代码示例:
// 数据服务 @Injectable({ providedIn: 'root' }) export class InitialDataService { private cachedData: any; private dataLoading$: Observable<any>; constructor(private http: HttpClient) {} getInitialData(): Observable<any> { // 存在缓存直接返回 if (this.cachedData) { return of(this.cachedData); } // 避免并发请求 if (this.dataLoading$) { return this.dataLoading$; } this.dataLoading$ = this.http.get('/api/initial-data').pipe( tap(data => { this.cachedData = data; this.dataLoading$ = null; }) ); return this.dataLoading$; } // 可选:提供清除缓存方法,用于需要刷新数据的场景 clearCache() { this.cachedData = null; } } // 修改后的Resolver @Injectable({ providedIn: 'root' }) export class InitialDataResolver implements Resolve<any> { constructor(private dataService: InitialDataService) {} resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<any> { return this.dataService.getInitialData(); } }
此方案不影响原有路由结构,同时兼容直接访问目标路由的场景。
方案二:调整路由结构,共享父路由解析数据
将根路径的Resolver移至更高层级的父路由,让重定向后的子路由继承父路由已加载的数据,移除目标路由上的Resolver配置。
调整后路由配置:
const routes: Routes = [ { path: '', component: PagesComponent, resolve: { initialData: initialDataResolver, }, children: [ { path: '', component: RedirectComponent, // 从父路由获取initialData执行重定向 }, { path: UserPath.Root, children: [ { path: 'user/organisations', component: UserOrganisationsComponent, // 直接从父路由route.data获取数据 } ], }, ], }, ];
需确保目标路由与根路由属于同一父路由分支,子路由可通过ActivatedRoute.parent?.data获取已解析的数据。
方案三:利用临时存储传递重定向数据
在根路径导航时临时存储接口返回数据,重定向后的Resolver检查到临时数据则直接返回,避免重复请求。
代码示例:
// 自定义临时存储服务 @Injectable({ providedIn: 'root' }) export class TempStorageService { private tempData = new Map<string, any>(); set(key: string, value: any) { this.tempData.set(key, value); } get(key: string): any { return this.tempData.get(key); } remove(key: string) { this.tempData.delete(key); } } // 修改后的Resolver @Injectable({ providedIn: 'root' }) export class InitialDataResolver implements Resolve<any> { constructor( private http: HttpClient, private tempStorage: TempStorageService ) {} resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<any> { const cachedData = this.tempStorage.get('initialData'); if (cachedData) { this.tempStorage.remove('initialData'); return of(cachedData); } // 根路径导航时临时存储数据 if (state.url === '/') { return this.http.get('/api/initial-data').pipe( tap(data => this.tempStorage.set('initialData', data)) ); } return this.http.get('/api/initial-data'); } }
此方案需注意临时数据的生命周期,避免数据污染,适合无法调整路由结构的场景。
内容的提问来源于stack exchange,提问作者Mike
相关产品推荐
相关产品推荐

