如何在独立仓库的两个Angular微前端应用间共享服务实例?
针对你在Nx微前端架构中遇到的远程应用重复创建UserService实例的问题,下面是几种无需依赖sessionStorage即可实现全局单实例的方案,按推荐优先级排序:
1. 利用Nx模块联邦配置共享单例依赖
Nx的动态加载基于Webpack模块联邦,通过配置共享依赖为单例模式,可确保整个应用集群中共享包仅加载一次,对应的服务自然保持单实例:
在宿主和所有远程应用的module-federation.config.js中,将你的用户共享包配置为singleton: true:
module.exports = { shared: { '@your-org/shared-user': { singleton: true, strictVersion: true, // 确保版本一致,避免多版本加载 requiredVersion: '^1.0.0' // 指定共享包的版本 } } };
配置后,模块联邦会强制所有应用复用同一个共享包的实例,无需修改服务代码即可实现全局单例。
2. 复用宿主应用的注入器实例
将UserService注册到宿主应用的全局注入器,远程应用直接从宿主注入器获取实例,避免自身创建新实例:
步骤1:宿主应用暴露注入器
在宿主的AppModule中注册UserService,并将注入器暴露到全局:
import { Injector, NgModule } from '@angular/core'; import { UserService } from '@your-org/shared-user'; // 全局注入器引用 export let appInjector: Injector; @NgModule({ providers: [UserService] // 注册为全局提供者 }) export class AppModule { constructor(private injector: Injector) { appInjector = injector; (window as any).appInjector = injector; // 暴露到window供远程应用访问 } }
步骤2:远程应用复用宿主实例
远程应用不要在自身模块中注册UserService,而是通过宿主注入器获取实例:
// 远程应用的模块配置 import { NgModule } from '@angular/core'; import { UserService } from '@your-org/shared-user'; @NgModule({ providers: [ { provide: UserService, useFactory: () => (window as any).appInjector.get(UserService) } ] }) export class RemoteAppModule {}
此后远程应用中通过依赖注入获取的UserService,均为宿主的单实例。
3. 基于全局RxJS Subject共享状态
如果仅需共享用户信息和事件,可在共享包中使用全局挂载的RxJS Subject,即使服务实例多,状态和事件也能保持统一:
import { Injectable } from '@angular/core'; import { BehaviorSubject, Observable } from 'rxjs'; // 全局共享的Subject,确保仅初始化一次 const globalUserSubject = (window as any).globalUserSubject || new BehaviorSubject<any>(null); (window as any).globalUserSubject = globalUserSubject; @Injectable() export class UserService { private userSubject = globalUserSubject; user$: Observable<any> = this.userSubject.asObservable(); setUser(user: any) { this.userSubject.next(user); } getUser() { return this.userSubject.value; } }
这种方式无需修改模块配置,所有应用的UserService都会订阅同一个全局流,状态和事件完全同步。
关于sessionStorage的补充
如果需要跨标签页共享状态,或页面刷新后保留用户信息,可以将上述方案与sessionStorage结合(比如在UserService的setUser方法中同步写入sessionStorage,初始化时从sessionStorage读取),但同页面内的微前端共享优先使用上面的方案,避免频繁读写存储带来的性能损耗。
内容的提问来源于stack exchange,提问作者StefanM

