Angular13登录跳转后导航栏不显示问题排查
问题背景
基于Angular 13搭建的可正常运行的应用,需新增身份认证作为全站访问前置校验,预期路由规则如下:
- 应用首次加载时进入登录页,不展示导航栏
- 登录成功后加载应用主体与导航栏
- 认证token过期时,所有路由操作强制跳转至登录页
- 点击退出登录按钮时跳转回登录页
目前第1、4项规则已通过AuthGuards机制实现,存在问题为:登录成功后从登录页跳转至首页时导航栏无法正常展示。
现有实现代码
app.component.html
<div> <app-navbar *ngIf="isLoggedIn" [isLoggedIn]="this.isLoggedIn"></app-navbar> <router-outlet></router-outlet> </div>
app.component.ts
ngOnInit(): void { if (this.cacheService.load("auth-token")) { this.isLoggedIn = true; } }
app-routing.module.ts
import { NgModule } from '@angular/core'; .... const routes: Routes = [ { path: 'login', component: LoginComponent }, { path: '', component: HomeComponent, canActivate: [AuthGuardService] }, { path: 'authed', component: AuthedComponent, canActivate: [AuthGuardService] }, { path: '**', redirectTo: '', pathMatch: 'full' } ]; @NgModule({ imports: [RouterModule.forRoot(routes)], exports: [RouterModule] }) export class AppRoutingModule { }
login.component.ts
ngOnInit(): void { if (this.cacheService.load("auth-token")) { this.isLoggedIn = true; } } onSubmit(): void { const { username, password } = this.form; const userReq: IUserRequest = { Email: username, Password: password, }; this.userService.login(userReq).subscribe({ next: res => { console.log(res); this.isLoggedIn = res.Success == true; this.isLoginFailed = false; this.route.navigate(['']); }, error: err => { this.errorMessage = err; this.isLoginFailed = true; } }); }
路由守卫(标注文件名auth-interceptor.ts,实际为canActivate守卫逻辑)
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree { if (this.cacheService.load('auth-token')) { return true; } this.route.navigate(['login']); return false; }
异常表现
认证成功后页面主体可正常加载,但导航栏不展示,手动刷新页面后导航栏才正常显示;尝试在ngOnInit中添加window.location.reload会触发页面无限刷新,多次修复无效。
问题根因
登录状态在组件间孤立维护,没有实现全局同步:
- AppComponent作为根组件,
isLoggedIn变量仅在自身ngOnInit生命周期(应用首次初始化、整页刷新时触发)执行一次本地token检查。登录成功时,你在LoginComponent中修改的是登录组件实例下的isLoggedIn属性,和AppComponent里的同名属性完全无关,因此模板中*ngIf="isLoggedIn"判断始终为false,导航栏无法渲染。 - 手动刷新页面时AppComponent重新初始化,
ngOnInit读取到本地存储的token将isLoggedIn设为true,导航栏才会正常显示,和观察到的现象完全一致。
修复方案
不要在每个组件单独维护登录状态变量,通过全局服务统一管理登录状态,实现状态变更的实时通知:
1. 扩展全局缓存服务,增加状态订阅能力
在现有CacheService中引入BehaviorSubject作为状态广播载体,所有组件统一从该服务获取登录状态:
import { Injectable } from '@angular/core'; import { BehaviorSubject } from 'rxjs'; @Injectable({ providedIn: 'root' }) export class CacheService { // 初始化时直接读取本地token作为初始登录状态 private isLoggedInSub = new BehaviorSubject<boolean>(!!this.load("auth-token")); // 对外暴露可订阅的状态流 public isLoggedIn$ = this.isLoggedInSub.asObservable(); // 保留原有缓存读写方法 load(key: string): string | null { return localStorage.getItem(key); } save(key: string, value: string): void { localStorage.setItem(key, value); } remove(key: string): void { localStorage.removeItem(key); } // 新增全局登录状态更新方法 setLoginStatus(status: boolean): void { this.isLoggedInSub.next(status); } }
2. 修改AppComponent,订阅全局登录状态
移除组件内独立的token检查逻辑,改为订阅服务的状态流,实时同步登录状态到本地变量:
import { Component, OnInit } from '@angular/core'; import { CacheService } from './services/cache.service'; // 替换为实际服务路径 @Component({ selector: 'app-root', templateUrl: './app.component.html' }) export class AppComponent implements OnInit { isLoggedIn = false; constructor(private cacheService: CacheService) {} ngOnInit(): void { // 订阅状态变更,任何地方更新登录状态都会触发本地变量同步 this.cacheService.isLoggedIn$.subscribe(status => { this.isLoggedIn = status; }); } }
同步简化模板代码,Angular模板中访问组件属性不需要加this前缀:
<div> <app-navbar *ngIf="isLoggedIn" [isLoggedIn]="isLoggedIn"></app-navbar> <router-outlet></router-outlet> </div>
3. 修改LoginComponent,登录成功时更新全局状态
登录成功后不再修改组件局部变量,而是调用服务方法更新全局登录状态,注意确认登录接口返回的token已经存入本地缓存:
onSubmit(): void { const { username, password } = this.form; const userReq: IUserRequest = { Email: username, Password: password, }; this.userService.login(userReq).subscribe({ next: res => { const loginSuccess = res.Success === true; this.isLoginFailed = !loginSuccess; if (loginSuccess) { // 如果userService.login没有自动存token,在这里补存 // this.cacheService.save("auth-token", res.Token); // 广播登录成功状态 this.cacheService.setLoginStatus(true); this.route.navigate(['']); } }, error: err => { this.errorMessage = err; this.isLoginFailed = true; this.cacheService.setLoginStatus(false); } }); }
4. 补全其他场景的状态同步
- 退出登录逻辑中,删除本地token后调用
this.cacheService.setLoginStatus(false),再跳转至登录页 - 拦截器检测到token过期时,先删除本地token、调用
this.cacheService.setLoginStatus(false),再执行登录页跳转
修改完成后,任何场景下的登录状态变更都会实时同步到AppComponent,导航栏会根据状态即时显示/隐藏,无需手动刷新页面,也不会出现无限刷新的问题。
内容的提问来源于stack exchange,提问作者Croz
相关产品推荐
相关产品推荐

