Angular 18 SSR中APP_INITIALIZER首次执行无法获取Cookie问题
问题描述
在启用SSR的Angular v18项目中,我通过APP_INITIALIZER使用ngx-cookie-service读取Cookie以初始化认证状态,已按SSR配置完成设置,但出现导航栏闪烁问题:页面加载时先显示「登录」按钮,随后立即切换为「我的账户」按钮。
服务器端终端输出false,浏览器控制台输出true,说明服务器端执行APP_INITIALIZER时未检测到Cookie,而客户端执行时能正确读取。
相关代码片段
app.config.ts
export const appConfig: ApplicationConfig = { providers: [ provideZoneChangeDetection({eventCoalescing: true}), provideRouter(routes), provideClientHydration(), provideHttpClient(withFetch()), { provide: APP_INITIALIZER, useFactory: () => { const cookieService = inject(SsrCookieService); const authService = inject(AuthService); return () => new Promise((resolve, reject) => { const isAuthenticated = cookieService.check("idToken"); console.log(isAuthenticated); if (isAuthenticated) { const idToken = cookieService.get("idToken"); authService.setUserCredentials(idToken); console.log(authService.getIsAuthenticated()); } resolve(true); }); }, multi: true, } ] };
nav-bar.component.ts
private authService = inject(AuthService); isAuthenticated = this.authService.getIsAuthenticated();
nav-bar.component.html
@if (!isAuthenticated) { <button routerLink="login">登录</button> } @else { <button routerLink="profile">我的账户</button> }
AppComponent模板
<app-nav/> <router-outlet/> <app-footer/>
尝试过的方案
- 注入
DOCUMENT调用document.cookie,页面加载时触发NotYetImplemented错误 - 在根路由使用BaseComponent和Resolve,将认证状态作为必填输入传递给
<app-nav>,问题依旧
期望结果
AuthService中的public isAuthenticated: boolean = false能在应用首次渲染前变为true,彻底避免导航栏闪烁。
复现步骤
- 加载项目(若出现404,刷新页面即可)
- 点击「登录」按钮
- 留空字段并点击「继续」按钮,生成名为
idToken的Cookie - 刷新浏览器,会先看到「登录」按钮,随后自动切换为「我的账户」按钮
- 可选:通过开发者工具删除Cookie可恢复初始状态
解决方案
核心原因
SSR模式下,服务器端无法直接访问客户端Cookie,导致服务器执行APP_INITIALIZER时cookieService.check("idToken")返回false,生成的初始HTML为「登录」按钮;客户端hydration完成后,APP_INITIALIZER再次执行,此时能读取到Cookie并更新认证状态,触发重新渲染为「我的账户」,从而产生闪烁。
解决步骤
1. 完善服务器端Cookie传递配置
在server.ts中,将客户端请求的Cookie传递给SsrCookieService,确保服务器端能获取到认证Cookie:
// server.ts import { bootstrapApplication } from '@angular/platform-browser'; import { AppComponent } from './src/app/app.component'; import { appConfig } from './src/app/app.config'; import { renderApplication } from '@angular/platform-server'; import { SsrCookieService } from 'ngx-cookie-service'; export default async function render(url: string, document: string, req: Request) { const html = await renderApplication(AppComponent, { document, url, platformProviders: [ { provide: SsrCookieService, useFactory: () => { const cookieService = new SsrCookieService(); const cookies = req.headers.get('cookie'); if (cookies) { cookieService.setRawCookie(cookies); } return cookieService; }, }, ], providers: [...appConfig.providers], }); return html; }
2. 使用Angular Signal存储认证状态
将AuthService中的普通布尔值改为Signal,确保状态变化能被正确追踪,且服务器端的状态能同步到客户端:
// auth.service.ts import { Injectable, signal } from '@angular/core'; @Injectable({ providedIn: 'root' }) export class AuthService { private readonly _isAuthenticated = signal(false); public isAuthenticated = this._isAuthenticated.asReadonly(); setUserCredentials(idToken: string) { // 此处可添加token校验逻辑 this._isAuthenticated.set(true); } getIsAuthenticated() { return this._isAuthenticated(); } }
3. 调整导航栏组件的状态读取方式
直接引用Signal而非快照值,确保组件能响应状态变化:
// nav-bar.component.ts private authService = inject(AuthService); isAuthenticated = this.authService.isAuthenticated; // 直接绑定Signal
模板无需修改,Angular会自动处理Signal的订阅与更新。
内容的提问来源于stack exchange,提问作者lotario
相关产品推荐
相关产品推荐

