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

Angular 18+Native Federation:远程应用LocalStorage同步至宿主方案

微前端下NgRx localStorage跨端口同步问题

问题背景

我有一个包含三个独立Angular 18应用的monorepo,每个应用都有专属库。远程应用的库中使用NgRx,单独运行各应用时,数据可正常存储至localStorage。采用@angular-architects/native-federation@18.2.2搭建微前端架构,宿主应用(shell)运行在localhost:4200,App1在localhost:4201,App2在localhost:4202。

当从宿主加载App1时,由于端口不同,localStorage无法更新。需求是各应用保持独立状态,不共享状态,但需要在宿主中为每个应用更新对应的LocalStorage,让远程应用能读取并使用自身的LocalStorage数据。

当前NgRx代码如下:

import {
  ActionReducer,
  ActionReducerMap,
  MetaReducer,
  provideState,
} from '@ngrx/store';
import { localStorageSync } from 'ngrx-store-localstorage';

import { currentTaskReducer } from './reducer/Task.reducer';
import { toolboxReducer } from './reducer/toolbox.reducer';

export const reducers: ActionReducerMap<any> = {
  currentTask: currentTaskReducer,
  toolbox: toolboxReducer,
};

export function localStorageSyncReducer(
  reducer: ActionReducer<any>,
): ActionReducer<any> {
  return localStorageSync({
    // add keys to sync with local storage
    keys: ['currentTask', 'toolbox'],
    rehydrate: true,
  })(reducer);
}

export const metaReducers: Array<MetaReducer<any, any>> = [
  localStorageSyncReducer,
];

解决方案

1. 为每个应用添加localStorage专属前缀

核心思路是通过给每个应用的存储键添加唯一前缀,实现数据隔离。修改ngrx-store-localstorage的配置,使用storageKeySerializer自定义存储键格式:

// App1的meta-reducer.ts
export function localStorageSyncReducer(
  reducer: ActionReducer<any>,
): ActionReducer<any> {
  return localStorageSync({
    keys: ['currentTask', 'toolbox'],
    rehydrate: true,
    // 为App1添加专属前缀
    storageKeySerializer: (key) => `app1_${key}`,
  })(reducer);
}

同理,App2的配置将前缀改为app2_,这样两个应用的localStorage数据会分别以app1_currentTask、app2_toolbox等键存储,完全隔离。

2. 结合Native Federation共享上下文动态设置前缀

如果需要宿主根据加载的应用动态传递标识,可通过Native Federation共享上下文服务实现:

步骤1:宿主配置共享上下文服务

在宿主的federation.config.js中添加共享配置:

module.exports = {
  name: 'shell',
  exposes: {},
  shared: {
    '@angular/core': { singleton: true, strictVersion: true },
    '@angular/common': { singleton: true, strictVersion: true },
    // 共享应用上下文服务
    './app-context': { singleton: true, import: () => import('./src/app/app-context.service') },
  },
};

步骤2:远程应用导入共享服务

在App1的federation.config.js中配置导入:

module.exports = {
  name: 'app1',
  exposes: {
    './Module': './src/app/app1.module.ts',
  },
  shared: {
    '@angular/core': { singleton: true, strictVersion: true },
    '@angular/common': { singleton: true, strictVersion: true },
    './app-context': { singleton: true, import: () => import('shell/app-context') },
  },
};

步骤3:实现上下文服务

宿主中创建上下文服务,用于传递当前加载的应用标识:

// src/app/app-context.service.ts
import { Injectable } from '@angular/core';

@Injectable({ providedIn: 'root' })
export class AppContextService {
  currentAppId: string = '';

  setCurrentAppId(appId: string) {
    this.currentAppId = appId;
  }
}

步骤4:远程应用动态获取前缀

在App1的meta-reducer中,注入上下文服务动态生成存储键:

// App1的meta-reducer.ts
import { inject } from '@angular/core';
import { ActionReducer } from '@ngrx/store';
import { localStorageSync } from 'ngrx-store-localstorage';
import { AppContextService } from 'shell/app-context';

export function localStorageSyncReducer(
  reducer: ActionReducer<any>,
): ActionReducer<any> {
  const appContext = inject(AppContextService);
  return localStorageSync({
    keys: ['currentTask', 'toolbox'],
    rehydrate: true,
    storageKeySerializer: (key) => `${appContext.currentAppId}_${key}`,
  })(reducer);
}

3. 验证效果

  • 单独运行App1时,localStorage会生成app1_currentTask等键;
  • 宿主加载App1时,远程应用会读取对应前缀的键,确保状态独立且能正确读写自身数据;
  • App2的配置同理,数据完全隔离。

内容的提问来源于stack exchange,提问作者vsam

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 04:22:34