如何在React中通过Module Federation实现MFE PWA与Shell共享Service Worker?
实现Shell与MFE之间共享Service Worker的方案(基于Module Federation + React)
首先明确核心限制:Service Worker的作用域由其文件位置决定,默认只能控制当前目录及子路径;且Module Federation无法直接导出SW文件本身,只能导出注册逻辑和核心业务模块。以下是具体实现步骤与关键配置:
1. 改造MFE的PWA配置,导出可复用的SW逻辑
假设MFE使用Vite构建(CRA可通过craco适配类似逻辑):
MFE的vite.config.js配置
import { defineConfig } from 'vite'; import react from '@vitejs/plugin-react'; import { VitePWA } from 'vite-plugin-pwa'; import federation from '@originjs/vite-plugin-federation'; export default defineConfig({ plugins: [ react(), // 配置Module Federation,导出SW注册函数和核心逻辑 federation({ name: 'mfePwa', filename: 'remoteEntry.js', exposes: { './SwRegister': './src/sw-register.js', './SwCore': './src/sw-core.js' }, shared: ['react', 'react-dom'] }), // PWA插件配置,生成可跨作用域的SW VitePWA({ srcDir: 'src', filename: 'sw.js', strategies: 'injectManifest', // 手动控制SW逻辑,而非默认的generateSW manifest: false, // 根据需求开启,若Shell有自己的manifest可关闭 workbox: { globPatterns: ['**/*.{js,css,html,ico,png,svg}'], scope: '/', // 让SW作用域覆盖根路径,包含Shell应用 navigateFallbackDenylist: [/^\/shell-specific-path/] // 排除Shell专属路径(可选) } }) ] });
抽离SW核心逻辑(src/sw-core.js)
把缓存策略、事件处理等通用逻辑抽成模块,方便Shell复用或扩展:
export const CACHE_NAME = 'mfe-pwa-shared-cache-v1'; export const PRECACHE_ASSETS = [ // MFE需要预缓存的静态资源 '/mfe-app.js', '/mfe-styles.css' ]; // 通用fetch拦截逻辑 export const handleFetchEvent = (event) => { event.respondWith( caches.match(event.request) .then(cachedRes => cachedRes || fetch(event.request)) ); };
SW入口文件(src/sw.js)
导入核心逻辑,实现标准SW生命周期:
import { CACHE_NAME, PRECACHE_ASSETS, handleFetchEvent } from './sw-core.js'; self.addEventListener('install', (event) => { event.waitUntil( caches.open(CACHE_NAME) .then(cache => cache.addAll(PRECACHE_ASSETS)) ); }); self.addEventListener('fetch', handleFetchEvent); // 可选:监听来自Shell的消息,动态调整缓存策略 self.addEventListener('message', (event) => { if (event.data.type === 'ADD_SHELL_ASSETS') { caches.open(CACHE_NAME) .then(cache => cache.addAll(event.data.assets)); } });
SW注册函数(src/sw-register.js)
导出注册SW的异步函数,让Shell直接调用:
export const registerSharedSw = async () => { if (!('serviceWorker' in navigator)) return; try { const registration = await navigator.serviceWorker.register('/sw.js', { scope: '/' // 强制作用域覆盖根路径 }); // 监听SW更新事件,通知用户刷新 registration.addEventListener('updatefound', () => { const newWorker = registration.installing; newWorker.addEventListener('statechange', () => { if (newWorker.state === 'installed' && navigator.serviceWorker.controller) { console.log('新内容已就绪,请刷新页面'); } }); }); } catch (err) { console.error('Service Worker注册失败:', err); } };
2. Shell应用中导入并使用MFE的SW
Shell的vite.config.js配置
添加MFE的remote地址:
import { defineConfig } from 'vite'; import react from '@vitejs/plugin-react'; import federation from '@originjs/vite-plugin-federation'; export default defineConfig({ plugins: [ react(), federation({ name: 'shell', remotes: { mfePwa: 'http://localhost:5001/remoteEntry.js' // MFE的remoteEntry地址 }, shared: ['react', 'react-dom'] }) ] });
在Shell入口注册SW
在src/main.jsx中导入并执行注册函数:
import React from 'react'; import ReactDOM from 'react-dom/client'; import App from './App'; import { registerSharedSw } from 'mfePwa/SwRegister'; ReactDOM.createRoot(document.getElementById('root')).render( <React.StrictMode> <App /> </React.StrictMode> ); // 注册共享SW registerSharedSw();
可选:扩展SW功能
如果Shell需要添加自己的静态资源到缓存,可通过SW的消息机制传递:
// Shell的某个组件或入口中 if ('serviceWorker' in navigator) { navigator.serviceWorker.ready.then(registration => { registration.active.postMessage({ type: 'ADD_SHELL_ASSETS', assets: ['/shell-logo.png', '/shell-common.css'] }); }); }
3. 关键注意事项
- 作用域与跨域配置:
- 若Shell与MFE部署在同一域名下,确保SW文件放在根路径,且配置
scope: '/'。 - 若为不同域名,需在MFE的服务器上添加响应头:
Service-Worker-Allowed: /,同时Shell需部署在HTTPS环境(localhost除外)。
- 若Shell与MFE部署在同一域名下,确保SW文件放在根路径,且配置
- 缓存冲突避免:统一缓存命名规则,比如给Shell和MFE的缓存名称加前缀,避免互相覆盖。
- Module Federation版本兼容:确保Shell与MFE使用的MF插件版本一致,共享依赖(React、ReactDOM)的版本范围兼容。
- HTTPS要求:生产环境必须使用HTTPS,否则Service Worker无法注册。
4. 排查常见问题
- 注册失败:检查SW文件路径是否正确,服务器是否返回200,作用域配置是否覆盖Shell路径。
- 缓存不生效:打开浏览器DevTools → Application → Cache Storage,查看缓存是否正确添加;检查fetch事件逻辑是否有误。
- MF导入失败:确认remoteEntry地址可访问,Shell与MFE的MF配置中
name和exposes名称匹配。
内容的提问来源于stack exchange,提问作者vaisakh raveendran
相关产品推荐
相关产品推荐

