Angular 16 SSR集成TranslationService出现NetworkError求助
Angular 16 SSR下注入TranslationService触发NetworkError问题
我使用Angular 16结合Angular/Universal实现服务端渲染(SSR),开发了TranslationService服务。在AppComponent的构造函数中注入该服务后,触发了NetworkError错误,错误栈如下:
NetworkError at send (./node_modules/xhr2/lib/xhr2.js:281:19) at observer (./node_modules/@angular/common/fesm2022/http.mjs:2187:21) at _trySubscribe (./node_modules/rxjs/dist/cjs/internal/Observable.js:41:25) at cb (./node_modules/rxjs/dist/cjs/internal/Observable.js:35:31) at errorContext (./node_modules/rxjs/dist/cjs/internal/util/errorContext.js:22:9) at subscribe (./node_modules/rxjs/dist/cjs/internal/Observable.js:26:24) at onNext (./node_modules/rxjs/dist/cjs/internal/operators/switchMap.js:17:63) at _next (./node_modules/rxjs/dist/cjs/internal/operators/OperatorSubscriber.js:33:21) at next (./node_modules/rxjs/dist/cjs/internal/Subscriber.js:51:18) at <anonymous> (./node_modules/rxjs/dist/cjs/internal/observable/innerFrom.js:120:28)
项目在客户端运行完全正常,但启用SSR时出现该错误。以下是相关代码文件:
TranslationService代码
import { Injectable, ViewChild, AfterViewInit } from '@angular/core'; import { TranslateService } from '@ngx-translate/core'; // import { AngularFirestore } from '@angular/fire/compat/firestore'; import { BehaviorSubject } from 'rxjs'; @Injectable({ providedIn: 'root', }) export class TranslationService implements AfterViewInit { public lang = new BehaviorSubject<any>(''); value: any; @ViewChild('body') body: any; constructor(public translate: TranslateService) { this.lang.next('en'); } ngAfterViewInit(): void {} changeLanguage(value: any) { this.translate.setDefaultLang(value); localStorage.setItem('lang', value); this.lang.next(value); this.value = value; if (value == 'ar') { document.documentElement.style.setProperty('direction', 'rtl'); document.documentElement.style.setProperty('overflow-x', 'hidden'); document.documentElement.style.setProperty('width', '101%'); } else { document.documentElement.style.setProperty('direction', 'ltr'); document.documentElement.style.setProperty('width', '100%'); } } }
AppModule代码
import { NgModule } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; import { environment } from 'src/environments/environment'; import { AngularFireModule } from '@angular/fire/compat'; import { AngularFirestoreModule } from '@angular/fire/compat/firestore'; import { AppRoutingModule } from './app-routing.module'; import { AppComponent } from './app.component'; import { CoreService } from '../services/core.service'; import { HttpClientModule, HttpClient } from '@angular/common/http'; import { TranslateModule, TranslateLoader, TranslateStore, } from '@ngx-translate/core'; import { TranslateHttpLoader } from '@ngx-translate/http-loader'; export function HttpLoaderFactory(http: HttpClient) { return new TranslateHttpLoader(http); } @NgModule({ declarations: [AppComponent], imports: [ BrowserModule, AngularFireModule.initializeApp(environment.firebase), AngularFirestoreModule, AppRoutingModule, HttpClientModule, TranslateModule.forChild({ defaultLanguage: 'en', loader: { provide: TranslateLoader, useFactory: HttpLoaderFactory, deps: [HttpClient], }, }), ], providers: [CoreService, TranslateStore], bootstrap: [AppComponent], }) export class AppModule {}
package.json代码
{ "version": "0.0.0", "scripts": { "ng": "ng", "start": "ng serve", "build": "ng build", "watch": "ng build --watch --configuration development", "test": "ng test", "dev:ssr": "ng run angular-app-for-ssr:serve-ssr", "serve:ssr": "node dist/angular-app-for-ssr/server/main.js", "build:ssr": "ng build && ng run angular-app-for-ssr:server", "prerender": "ng run angular-app-for-ssr:prerender" }, "private": true, "dependencies": { "@angular/animations": "^16.1.0", "@angular/common": "^16.1.0", "@angular/compiler": "^16.1.0", "@angular/core": "^16.1.0", "@angular/fire": "^7.5.0", "@angular/forms": "^16.1.0", "@angular/platform-browser": "^16.1.0", "@angular/platform-browser-dynamic": "^16.1.0", "@angular/platform-server": "^16.1.0", "@angular/router": "^16.1.0", "@nguniversal/express-engine": "^16.1.1", "@ngx-translate/core": "15.0.0", "@ngx-translate/http-loader": "8.0.0", "express": "^4.15.2", "rxjs": "~7.8.0", "tslib": "^2.3.0", "zone.js": "~0.13.0" } }
问题原因
- SSR资源路径错误:
TranslateHttpLoader默认请求/assets/i18n/{lang}.json,但服务端运行时,相对路径无法正确定位静态资源,导致网络请求失败。 - 浏览器API滥用:TranslationService直接操作
localStorage、document等浏览器专属API,服务端环境不存在这些对象,不仅会引发后续错误,也会干扰SSR渲染流程。 - TranslateModule配置错误:根模块使用
TranslateModule.forChild()而非forRoot(),导致服务端与客户端的翻译服务初始化不一致。
解决步骤
1. 修正TranslateModule根配置
将AppModule中的TranslateModule.forChild()替换为TranslateModule.forRoot(),确保翻译服务在根模块正确初始化:
TranslateModule.forRoot({ defaultLanguage: 'en', loader: { provide: TranslateLoader, useFactory: HttpLoaderFactory, deps: [HttpClient], }, }),
2. 调整翻译文件请求路径
修改HttpLoaderFactory,根据运行环境动态拼接资源路径,确保服务端能正确访问静态文件:
import { Inject, Optional } from '@angular/core'; import { REQUEST } from '@nguniversal/express-engine/tokens'; import { Request } from 'express'; export function HttpLoaderFactory(http: HttpClient, @Optional() @Inject(REQUEST) request: Request) { // 服务端通过请求对象获取当前域名,客户端用相对路径 const baseUrl = request ? `${request.protocol}://${request.get('host')}` : ''; return new TranslateHttpLoader(http, `${baseUrl}/assets/i18n/`, '.json'); }
同时更新TranslateModule中loader的deps配置:
deps: [HttpClient, [new Optional(), new Inject(REQUEST)]]
3. 封装浏览器API调用
在TranslationService中添加环境判断,仅在浏览器环境下操作专属API,同时移除服务中无效的组件装饰器与生命周期钩子:
import { Injectable } from '@angular/core'; import { TranslateService } from '@ngx-translate/core'; import { BehaviorSubject } from 'rxjs'; @Injectable({ providedIn: 'root', }) export class TranslationService { public lang = new BehaviorSubject<any>(''); value: any; constructor(public translate: TranslateService) { this.lang.next('en'); } // 判断是否为浏览器环境 private isBrowser(): boolean { return typeof window !== 'undefined' && typeof document !== 'undefined'; } changeLanguage(value: any) { this.translate.setDefaultLang(value); if (this.isBrowser()) { localStorage.setItem('lang', value); if (value == 'ar') { document.documentElement.style.setProperty('direction', 'rtl'); document.documentElement.style.setProperty('overflow-x', 'hidden'); document.documentElement.style.setProperty('width', '101%'); } else { document.documentElement.style.setProperty('direction', 'ltr'); document.documentElement.style.setProperty('width', '100%'); } } this.lang.next(value); this.value = value; } }
4. 服务端预加载翻译(可选)
若需SSR直接渲染对应语言内容,可通过Angular Universal的TransferState将服务端加载的翻译数据传递到客户端,避免重复请求,提升首屏渲染速度。
内容的提问来源于stack exchange,提问作者Syed Muhammad Ahmed
相关产品推荐
相关产品推荐

