Angular 13 SSR中ngOnInit因属性赋值多次调用的问题排查
Angular 13 SSR中ngOnInit多次调用的原因及解决
核心原因
在SSR(服务器端渲染)模式下,Angular会分两个阶段执行组件逻辑:
- 服务器端渲染阶段:Angular在服务器上初始化组件、执行
ngOnInit并渲染静态HTML,之后把页面发送给浏览器。 - 浏览器水合阶段:浏览器接收页面后,会重新初始化Angular应用,再次执行组件的
ngOnInit,完成页面的动态绑定。
你说的“赋值引发问题”,本质是这个双执行流程导致的——服务器端执行ngOnInit时发起请求并赋值data,浏览器端再次执行ngOnInit又发起一次请求并赋值,看起来像是赋值触发了多次调用,但根源是SSR的默认机制。如果data的赋值还触发了额外的变更检测,可能会让这个问题更明显。
其他可能的诱因
- 若模板中用了依赖
data的结构指令(比如*ngIf="data.length"),当data从空数组变为接口返回值时,可能触发组件重新渲染,配合SSR的双执行流程,会加剧多次调用的感知。 - 如果
getData()是同步返回数据,服务器端和浏览器端的赋值操作会立即触发,更容易察觉到多次执行的问题。
解决办法
区分环境执行请求:
用Angular的PLATFORM_ID令牌判断当前环境,只在浏览器端发起请求(或只在服务器端发起,再把数据传给浏览器):import { Component, OnInit, Inject, PLATFORM_ID } from '@angular/core'; import { isPlatformBrowser } from '@angular/common'; constructor(@Inject(PLATFORM_ID) private platformId: Object) {} ngOnInit() { if (isPlatformBrowser(this.platformId)) { this.service.getData().subscribe(res => this.data = res); } }用TransferState共享服务器端数据:
避免服务器和浏览器重复请求,把服务器端获取的数据通过TransferState传递到浏览器,浏览器直接复用数据:import { Component, OnInit, Inject, PLATFORM_ID } from '@angular/core'; import { isPlatformServer, isPlatformBrowser, TransferState, makeStateKey } from '@angular/common'; private DATA_KEY = makeStateKey<any>('app-data'); constructor( @Inject(PLATFORM_ID) private platformId: Object, private transferState: TransferState ) {} ngOnInit() { if (isPlatformServer(this.platformId)) { this.service.getData().subscribe(res => { this.data = res; this.transferState.set(this.DATA_KEY, res); }); } else { this.data = this.transferState.get(this.DATA_KEY, []); // 如果TransferState里没有数据,再发起请求 if (!this.data.length) { this.service.getData().subscribe(res => this.data = res); } } }检查组件是否被重复创建:
确认app.component是否在路由配置或其他地方被多次引用,导致组件实例被多次初始化。
内容的提问来源于stack exchange,提问作者user26571055
相关产品推荐
相关产品推荐

