You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Angular 18 SSR中APP_INITIALIZER首次执行无法获取Cookie问题

Angular SSR v18中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,
    }
  ]
};
private authService = inject(AuthService);
isAuthenticated = this.authService.getIsAuthenticated();
@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,彻底避免导航栏闪烁。

复现步骤

  1. 加载项目(若出现404,刷新页面即可)
  2. 点击「登录」按钮
  3. 留空字段并点击「继续」按钮,生成名为idToken的Cookie
  4. 刷新浏览器,会先看到「登录」按钮,随后自动切换为「我的账户」按钮
  5. 可选:通过开发者工具删除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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.16 16:07:39