Angular6路由出口渲染延迟问题求助:API未完成致sessionStorage数据无法读取
解决Angular 6中组件加载时sessionStorage数据未就绪的问题
嘿,作为Angular开发者,我完全懂你这个痛点——路由加载太快,API请求还没完成,组件就已经去读sessionStorage了,用Resolve又怕重复调用API对吧?给你几个可行的解决方案,按需选择:
方案一:用CanActivate路由守卫 + 共享服务(推荐)
这个方案能确保只有当API数据加载完成后,才允许进入任何需要该数据的路由,而且只会请求一次数据。
步骤如下:
- 创建一个共享数据服务,负责API请求和缓存数据:
// data.service.ts import { Injectable } from '@angular/core'; import { HttpClient } from '@angular/common/http'; import { Observable, of } from 'rxjs'; import { tap } from 'rxjs/operators'; @Injectable({ providedIn: 'root' }) export class DataService { private cachedData: any = null; constructor(private http: HttpClient) {} fetchData(): Observable<any> { // 如果已经缓存过数据,直接返回 if (this.cachedData) { return of(this.cachedData); } // 否则调用API,并存入sessionStorage和本地缓存 return this.http.get('你的API地址').pipe( tap(data => { this.cachedData = data; sessionStorage.setItem('appData', JSON.stringify(data)); }) ); } }
- 创建一个CanActivate守卫,在路由激活前确保数据已加载:
// data.guard.ts import { Injectable } from '@angular/core'; import { CanActivate, Router, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router'; import { DataService } from './data.service'; import { Observable } from 'rxjs'; import { tap, map } from 'rxjs/operators'; @Injectable({ providedIn: 'root' }) export class DataGuard implements CanActivate { constructor(private dataService: DataService, private router: Router) {} canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> { return this.dataService.fetchData().pipe( map(data => !!data), // 确保数据存在 tap(hasData => { if (!hasData) { this.router.navigate(['/error']); // 加载失败可以跳错误页 } }) ); } }
- 在路由配置中,给所有需要该数据的路由加上这个守卫:
// app-routing.module.ts import { NgModule } from '@angular/core'; import { Routes, RouterModule } from '@angular/router'; import { AbcComponent } from './abc/abc.component'; import { DataGuard } from './data.guard'; const routes: Routes = [ { path: 'abc', component: AbcComponent, canActivate: [DataGuard] }, // 其他需要数据的路由也加上canActivate: [DataGuard] ]; @NgModule({ imports: [RouterModule.forRoot(routes)], exports: [RouterModule] }) export class AppRoutingModule { }
这样不管哪个组件通过router-outlet加载,只要路由配置了这个守卫,都会先确保数据已加载完成,而且API只会被调用一次。
方案二:在AppComponent中控制router-outlet的显示时机
这个思路很直接:等API请求完成后,再显示router-outlet,这样后续加载的组件肯定能读到sessionStorage里的数据。
- 在AppComponent中处理API请求和加载状态:
// app.component.ts import { Component, OnInit } from '@angular/core'; import { HttpClient } from '@angular/common/http'; @Component({ selector: 'app-root', templateUrl: './app.component.html' }) export class AppComponent implements OnInit { isDataLoaded = false; constructor(private http: HttpClient) {} ngOnInit() { this.http.get('你的API地址').subscribe(data => { sessionStorage.setItem('appData', JSON.stringify(data)); this.isDataLoaded = true; // 数据加载完成,允许显示路由 }, error => { // 处理加载失败的情况,比如显示错误提示 console.error('数据加载失败', error); this.isDataLoaded = true; // 或者根据需求决定是否允许进入路由 }); } }
- 在AppComponent的模板中,用*ngIf控制router-outlet的显示:
<!-- app.component.html --> <div *ngIf="!isDataLoaded">加载中...</div> <router-outlet *ngIf="isDataLoaded"></router-outlet>
这个方案简单粗暴,适合所有组件都依赖这份数据的场景,缺点是用户会先看到加载状态,直到数据完成才会显示路由内容。
方案三:优化Resolve守卫,避免重复请求
如果你还是想用Resolve,那可以让它只在数据不存在时才调用API,否则直接返回缓存数据。
- 同样用上面的DataService(带缓存逻辑),然后创建Resolve:
// data.resolve.ts import { Injectable } from '@angular/core'; import { Resolve, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router'; import { DataService } from './data.service'; import { Observable } from 'rxjs'; @Injectable({ providedIn: 'root' }) export class DataResolve implements Resolve<any> { constructor(private dataService: DataService) {} resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<any> { // 调用带缓存的fetchData方法,只会请求一次API return this.dataService.fetchData(); } }
- 在路由配置中使用这个Resolve:
// app-routing.module.ts const routes: Routes = [ { path: 'abc', component: AbcComponent, resolve: { appData: DataResolve } }, // 其他路由也可以用这个Resolve ];
这样每个路由的Resolve都会调用DataService的fetchData,但因为服务里有缓存,所以API只会被调用一次,后续都是直接返回缓存数据。组件里可以通过this.route.snapshot.data['appData']获取数据,也可以直接读sessionStorage。
内容的提问来源于stack exchange,提问作者Lokesh
相关产品推荐
相关产品推荐

