乐闻世界logo
搜索文章和话题

Module Federation

Module Federation 是 Webpack 5 引入的一项革命性功能,旨在实现不同前端应用之间的模块共享和动态加载,从而促进微前端架构的发展。它允许多个独立构建的应用在运行时共享代码和依赖,无需预先打包到单一应用中,极大地降低了代码冗余和版本冲突的风险。通过 Module Federation,应用可以暴露自己的模块供其他应用动态加载,同时也能远程加载其他应用暴露的模块,实现跨应用的资源复用。其核心优势包括支持异步加载、版本兼容管理和独立部署,极大提升了大型项目的灵活性和扩展性。开发者可以通过简单配置,指定哪些模块需要共享,哪些模块是远程加载,配合 Webpack 的构建流程无缝集成。Module Federation 不仅适用于微前端场景,也适合多团队协作、插件化架构和动态功能扩展等多种应用场景,帮助团队实现更高效的代码复用和更灵活的系统演进。
Module Federation
查看更多相关内容
Module Federation 的部署策略有哪些?如何进行生产环境部署?Module Federation 的部署策略对于生产环境的稳定性和性能至关重要。以下是详细的部署方案: **1. 部署架构设计** **独立部署模式:** ``` ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ Host App │ │ Remote App 1│ │ Remote App 2│ │ :3000 │ │ :3001 │ │ :3002 │ └──────┬──────┘ └──────┬──────┘ └──────┬──────┘ │ │ │ └───────────────────┼───────────────────┘ │ ┌──────▼──────┐ │ Load Balancer│ └──────┬──────┘ │ ┌──────▼──────┐ │ CDN / Nginx│ └─────────────┘ ``` **2. 环境配置管理** **开发环境配置:** ```javascript // webpack.config.js const isDevelopment = process.env.NODE_ENV === 'development' module.exports = { plugins: [ new ModuleFederationPlugin({ name: 'app1', filename: 'remoteEntry.js', remotes: { app2: isDevelopment ? 'app2@http://localhost:3002/remoteEntry.js' : 'app2@https://app2.example.com/remoteEntry.js' }, shared: { react: { singleton: true, eager: true }, 'react-dom': { singleton: true, eager: true } } }) ] } ``` **生产环境配置:** ```javascript // .env.production REMOTE_APP_URL=https://cdn.example.com REMOTE_APP_VERSION=1.2.3 // webpack.config.js const remoteUrl = process.env.REMOTE_APP_URL const remoteVersion = process.env.REMOTE_APP_VERSION module.exports = { plugins: [ new ModuleFederationPlugin({ name: 'app1', remotes: { app2: `app2@${remoteUrl}/app2/${remoteVersion}/remoteEntry.js` } }) ] } ``` **3. CDN 部署策略** **静态资源上传:** ```bash # 使用 AWS S3 + CloudFront aws s3 sync ./dist s3://my-bucket/app1/1.0.0/ --delete aws cloudfront create-invalidation --distribution-id E1234567890 --paths "/*" # 使用阿里云 OSS ossutil cp -rf ./dist oss://my-bucket/app1/1.0.0/ # 使用腾讯云 COS coscli cp -r ./dist cos://my-bucket/app1/1.0.0/ ``` **Nginx 配置:** ```nginx server { listen 80; server_name app1.example.com; # 主应用 location / { root /var/www/app1; try_files $uri $uri/ /index.html; } # 远程模块入口 location /remoteEntry.js { root /var/www/app1; add_header Cache-Control "public, max-age=31536000, immutable"; } # 模块文件 location /assets/ { root /var/www/app1; add_header Cache-Control "public, max-age=31536000, immutable"; } # 启用 gzip 压缩 gzip on; gzip_types text/javascript application/javascript; } ``` **4. 版本管理策略** **语义化版本控制:** ```json { "version": "1.2.3", "scripts": { "version:patch": "npm version patch && npm run deploy", "version:minor": "npm version minor && npm run deploy", "version:major": "npm version major && npm run deploy" } } ``` **版本回滚机制:** ```bash # 部署脚本 deploy.sh #!/bin/bash VERSION=$1 BACKUP_DIR="/var/backups/app1" CURRENT_DIR="/var/www/app1" # 备份当前版本 if [ -d "$CURRENT_DIR" ]; then cp -r "$CURRENT_DIR" "$BACKUP_DIR/$(date +%Y%m%d_%H%M%S)" fi # 部署新版本 cp -r "./dist" "$CURRENT_DIR" # 健康检查 if ! curl -f http://localhost:3000/health; then echo "Health check failed, rolling back..." cp -r "$BACKUP_DIR/latest" "$CURRENT_DIR" exit 1 fi echo "Deployment successful" ``` **5. CI/CD 集成** **GitHub Actions 配置:** ```yaml name: Deploy Module Federation on: push: branches: [main] jobs: build-and-deploy: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - name: Setup Node.js uses: actions/setup-node@v2 with: node-version: '16' - name: Install dependencies run: npm ci - name: Build run: npm run build - name: Run tests run: npm test - name: Deploy to S3 uses: jakejarvis/s3-sync-action@master with: args: --delete env: AWS_S3_BUCKET: ${{ secrets.AWS_S3_BUCKET }} AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} AWS_REGION: 'us-east-1' SOURCE_DIR: 'dist' - name: Invalidate CloudFront uses: chetan/invalidate-cloudfront-action@master env: DISTRIBUTION: ${{ secrets.CLOUDFRONT_DISTRIBUTION_ID }} PATHS: '/*' AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} AWS_REGION: 'us-east-1' ``` **6. 监控和告警** **健康检查端点:** ```javascript // health.js app.get('/health', (req, res) => { const health = { status: 'ok', timestamp: new Date().toISOString(), uptime: process.uptime(), memory: process.memoryUsage(), remoteModules: { app2: { loaded: true, version: '1.2.3', lastUpdated: new Date().toISOString() } } } res.json(health) }) ``` **监控指标:** ```javascript // metrics.js const Prometheus = require('prom-client') const httpRequestDuration = new Prometheus.Histogram({ name: 'http_request_duration_seconds', help: 'Duration of HTTP requests in seconds', labelNames: ['method', 'route', 'code'] }) const moduleLoadDuration = new Prometheus.Histogram({ name: 'module_load_duration_seconds', help: 'Duration of module loading in seconds', labelNames: ['module', 'status'] }) // 使用示例 const start = Date.now() await import('remoteApp/Module') moduleLoadDuration.observe( { module: 'remoteApp/Module', status: 'success' }, (Date.now() - start) / 1000 ) ``` **7. 灾难恢复** **备份策略:** ```bash # 定期备份脚本 #!/bin/bash BACKUP_DIR="/backups/$(date +%Y%m%d)" mkdir -p "$BACKUP_DIR" # 备份所有应用 for app in app1 app2 app3; do cp -r "/var/www/$app" "$BACKUP_DIR/" done # 上传到 S3 aws s3 sync "$BACKUP_DIR" s3://my-backups/ ``` **故障转移配置:** ```nginx upstream app1_cluster { server app1-primary.example.com weight=3; server app1-backup.example.com weight=1; server app1-dr.example.com backup; } server { location / { proxy_pass http://app1_cluster; proxy_next_upstream error timeout invalid_header http_500 http_502 http_503; proxy_next_upstream_tries 2; } } ``` **最佳实践总结:** 1. **环境隔离**:开发、测试、生产环境完全隔离 2. **版本管理**:使用语义化版本,支持快速回滚 3. **CDN 加速**:使用 CDN 分发静态资源,提升加载速度 4. **自动化部署**:集成 CI/CD,实现自动化部署流程 5. **监控告警**:实时监控应用状态,及时发现和解决问题 6. **备份恢复**:定期备份,制定灾难恢复计划 7. **灰度发布**:支持灰度发布,降低风险 8. **文档完善**:维护详细的部署文档和操作手册 通过以上部署策略,可以确保 Module Federation 应用在生产环境中的稳定性和可靠性。
前端 · 2026年2月19日 17:46
Module Federation 如何实现动态加载?有哪些优势?Module Federation 的动态加载是指在运行时根据需要动态导入远程模块,而不是在构建时静态确定所有依赖。以下是详细说明: **动态导入语法:** ```javascript // 基本动态导入 const RemoteButton = React.lazy(() => import('remoteApp/Button') ) // 带错误处理的动态导入 const loadRemoteModule = async () => { try { const module = await import('remoteApp/Button') return module.default } catch (error) { console.error('Failed to load remote module:', error) return FallbackComponent } } ``` **动态加载的优势:** 1. **按需加载**:只在需要时才加载远程模块,减少初始加载时间 2. **灵活性**:可以根据用户权限、环境等条件动态决定加载哪些模块 3. **性能优化**:避免加载用户不需要的功能,提升整体性能 4. **独立部署**:远程模块可以独立更新,无需重新部署主应用 **实现原理:** Module Federation 使用 Webpack 的动态导入(dynamic import)机制,结合容器插件实现: 1. **入口文件加载**:首先加载远程应用的 remoteEntry.js 2. **模块解析**:通过 remoteEntry.js 解析模块映射关系 3. **异步加载**:使用 import() 语法异步加载目标模块 4. **依赖注入**:自动注入共享依赖,确保模块正常运行 **实际应用场景:** ```javascript // 场景1:根据路由动态加载 const routes = [ { path: '/dashboard', component: React.lazy(() => import('dashboardApp/Dashboard')) }, { path: '/settings', component: React.lazy(() => import('settingsApp/Settings')) } ] // 场景2:根据用户权限动态加载 const loadAdminPanel = async (isAdmin) => { if (isAdmin) { const AdminPanel = await import('adminApp/AdminPanel') return AdminPanel.default } return null } // 场景3:懒加载组件 function App() { const [RemoteComponent, setRemoteComponent] = useState(null) useEffect(() => { import('remoteApp/Feature') .then(module => setRemoteComponent(() => module.default)) .catch(error => console.error(error)) }, []) return ( <Suspense fallback={<Loading />}> {RemoteComponent && <RemoteComponent />} </Suspense> ) } ``` **错误处理和降级策略:** ```javascript // 完整的错误处理示例 const RemoteModule = React.lazy(() => import('remoteApp/Module') .catch(error => { console.error('Remote module load failed:', error) // 降级到本地模块 return import('./LocalFallback') }) ) ``` **性能优化技巧:** 1. **预加载**:在空闲时预加载可能需要的远程模块 2. **缓存策略**:合理设置 remoteEntry.js 的缓存策略 3. **代码分割**:远程模块内部也使用代码分割,进一步优化加载 4. **CDN 加速**:将 remoteEntry.js 和模块文件部署到 CDN **注意事项:** - 动态加载是异步的,需要配合 Suspense 或 async/await 使用 - 确保远程应用的入口文件可访问 - 处理网络错误和加载失败的情况 - 考虑添加加载状态和错误边界
前端 · 2026年2月19日 17:46
Module Federation 如何进行调试和问题排查?有哪些调试工具?Module Federation 的调试和问题排查是开发过程中的重要环节,以下是详细的调试方案: **1. 开发环境调试** **启用 Source Maps:** ```javascript // webpack.config.js module.exports = { devtool: 'source-map', output: { devtoolModuleFilenameTemplate: 'webpack://[namespace]/[resource-path]?[loaders]', devtoolFallbackModuleFilenameTemplate: 'webpack://[namespace]/[resource-path]?[loaders]' } } ``` **配置开发服务器:** ```javascript // webpack.config.js const HtmlWebpackPlugin = require('html-webpack-plugin') module.exports = { devServer: { port: 3000, hot: true, historyApiFallback: true, headers: { 'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, PATCH, OPTIONS', 'Access-Control-Allow-Headers': 'X-Requested-With, content-type, Authorization' }, client: { overlay: { errors: true, warnings: false } } }, plugins: [ new HtmlWebpackPlugin({ template: './public/index.html' }) ] } ``` **2. 模块加载调试** **模块加载追踪:** ```javascript // module-loader-debug.js class ModuleLoaderDebugger { constructor() { this.loadTimes = new Map() this.loadErrors = new Map() this.interceptImports() } interceptImports() { const originalImport = window.__webpack_require__ window.__webpack_require__ = (moduleId) => { const startTime = performance.now() try { const module = originalImport(moduleId) const loadTime = performance.now() - startTime this.loadTimes.set(moduleId, { loadTime, timestamp: Date.now() }) console.log(`✅ Module loaded: ${moduleId} (${loadTime.toFixed(2)}ms)`) return module } catch (error) { this.loadErrors.set(moduleId, { error, timestamp: Date.now() }) console.error(`❌ Module load failed: ${moduleId}`, error) throw error } } } getLoadStats() { return { totalModules: this.loadTimes.size, totalErrors: this.loadErrors.size, averageLoadTime: Array.from(this.loadTimes.values()) .reduce((sum, stat) => sum + stat.loadTime, 0) / this.loadTimes.size, slowModules: Array.from(this.loadTimes.entries()) .filter(([_, stat]) => stat.loadTime > 1000) .map(([id, stat]) => ({ id, loadTime: stat.loadTime })) } } } // 初始化调试器 if (process.env.NODE_ENV === 'development') { new ModuleLoaderDebugger() } ``` **3. 远程模块调试** **远程模块检查工具:** ```javascript // remote-module-debugger.js class RemoteModuleDebugger { constructor() { this.remoteModules = new Map() this.checkRemoteModules() } async checkRemoteModules() { const remotes = this.getRemoteConfig() for (const [name, config] of Object.entries(remotes)) { try { await this.checkRemoteModule(name, config) } catch (error) { console.error(`Remote module check failed: ${name}`, error) } } } getRemoteConfig() { // 从 webpack 配置中获取远程模块配置 return { remoteApp1: { url: 'http://localhost:3001/remoteEntry.js', scope: 'remoteApp1' }, remoteApp2: { url: 'http://localhost:3002/remoteEntry.js', scope: 'remoteApp2' } } } async checkRemoteModule(name, config) { console.log(`Checking remote module: ${name}`) // 检查远程入口文件是否可访问 const response = await fetch(config.url) if (!response.ok) { throw new Error(`Failed to fetch remote entry: ${response.status}`) } // 检查入口文件内容 const content = await response.text() if (!content.includes(config.scope)) { throw new Error(`Remote entry does not contain scope: ${config.scope}`) } // 尝试加载远程模块 try { const module = await import(`${name}/Module`) this.remoteModules.set(name, { status: 'loaded', module, timestamp: Date.now() }) console.log(`✅ Remote module loaded: ${name}`) } catch (error) { this.remoteModules.set(name, { status: 'error', error, timestamp: Date.now() }) console.error(`❌ Failed to load remote module: ${name}`, error) } } getModuleStatus(name) { return this.remoteModules.get(name) } getAllModuleStatus() { return Object.fromEntries(this.remoteModules) } } export const remoteModuleDebugger = new RemoteModuleDebugger() ``` **4. 共享依赖调试** **共享依赖检查工具:** ```javascript // shared-dependency-debugger.js class SharedDependencyDebugger { constructor() { this.sharedDependencies = new Map() this.checkSharedDependencies() } checkSharedDependencies() { const shared = this.getSharedConfig() for (const [name, config] of Object.entries(shared)) { this.checkSharedDependency(name, config) } } getSharedConfig() { // 从 webpack 配置中获取共享依赖配置 return { react: { singleton: true, requiredVersion: '^17.0.0', strictVersion: false }, 'react-dom': { singleton: true, requiredVersion: '^17.0.0', strictVersion: false } } } checkSharedDependency(name, config) { try { const dependency = require(name) const version = dependency.version const isSingleton = config.singleton const requiredVersion = config.requiredVersion const strictVersion = config.strictVersion const isCompatible = this.checkVersionCompatibility( version, requiredVersion, strictVersion ) this.sharedDependencies.set(name, { version, isSingleton, requiredVersion, isCompatible, status: isCompatible ? 'ok' : 'incompatible' }) if (isCompatible) { console.log(`✅ Shared dependency: ${name}@${version}`) } else { console.warn(`⚠️ Incompatible shared dependency: ${name}@${version} (required: ${requiredVersion})`) } } catch (error) { this.sharedDependencies.set(name, { status: 'error', error }) console.error(`❌ Failed to load shared dependency: ${name}`, error) } } checkVersionCompatibility(currentVersion, requiredVersion, strictVersion) { if (strictVersion) { return currentVersion === requiredVersion } // 使用 semver 检查版本兼容性 const semver = require('semver') return semver.satisfies(currentVersion, requiredVersion) } getDependencyStatus(name) { return this.sharedDependencies.get(name) } getAllDependencyStatus() { return Object.fromEntries(this.sharedDependencies) } } export const sharedDependencyDebugger = new SharedDependencyDebugger() ``` **5. 性能分析工具** **模块加载性能分析:** ```javascript // performance-analyzer.js class PerformanceAnalyzer { constructor() { this.metrics = new Map() this.setupPerformanceObserver() } setupPerformanceObserver() { if ('PerformanceObserver' in window) { const observer = new PerformanceObserver((list) => { list.getEntries().forEach((entry) => { if (entry.entryType === 'resource') { this.recordResourceLoad(entry) } }) }) observer.observe({ entryTypes: ['resource', 'measure'] }) } } recordResourceLoad(entry) { const { name, duration, transferSize } = entry if (name.includes('remoteEntry') || name.includes('node_modules')) { this.metrics.set(name, { duration, transferSize, timestamp: entry.startTime }) } } analyzeModuleLoad(moduleName) { const startTime = performance.now() return { start: () => startTime, end: () => { const endTime = performance.now() const duration = endTime - startTime this.metrics.set(moduleName, { duration, timestamp: startTime }) console.log(`📊 Module load time: ${moduleName} (${duration.toFixed(2)}ms)`) return duration } } } getPerformanceReport() { const metrics = Array.from(this.metrics.values()) return { totalLoadTime: metrics.reduce((sum, m) => sum + (m.duration || 0), 0), totalTransferSize: metrics.reduce((sum, m) => sum + (m.transferSize || 0), 0), averageLoadTime: metrics.reduce((sum, m) => sum + (m.duration || 0), 0) / metrics.length, slowResources: metrics.filter(m => m.duration > 1000), largeResources: metrics.filter(m => m.transferSize > 100000) } } } export const performanceAnalyzer = new PerformanceAnalyzer() ``` **6. 错误处理和日志** **全局错误处理:** ```javascript // error-handler.js class ErrorHandler { constructor() { this.errors = [] this.setupErrorHandlers() } setupErrorHandlers() { // 捕获模块加载错误 window.addEventListener('error', (event) => { if (event.filename && event.filename.includes('remoteEntry')) { this.handleModuleLoadError(event) } }) // 捕获未处理的 Promise 拒绝 window.addEventListener('unhandledrejection', (event) => { this.handleUnhandledRejection(event) }) } handleModuleLoadError(event) { const error = { type: 'module_load_error', message: event.message, filename: event.filename, lineno: event.lineno, colno: event.colno, stack: event.error?.stack, timestamp: Date.now() } this.errors.push(error) console.error('Module load error:', error) // 发送到错误追踪服务 this.sendToErrorTracking(error) } handleUnhandledRejection(event) { const error = { type: 'unhandled_rejection', message: event.reason?.message || String(event.reason), stack: event.reason?.stack, timestamp: Date.now() } this.errors.push(error) console.error('Unhandled rejection:', error) // 发送到错误追踪服务 this.sendToErrorTracking(error) } sendToErrorTracking(error) { // 发送到 Sentry 或其他错误追踪服务 if (window.Sentry) { window.Sentry.captureException(error) } // 或发送到自定义错误追踪端点 fetch('/api/error-tracking', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(error) }).catch(console.error) } getErrors() { return this.errors } clearErrors() { this.errors = [] } } export const errorHandler = new ErrorHandler() ``` **7. 调试工具集成** **React DevTools 集成:** ```javascript // debug-tools.js import React from 'react' import { createRoot } from 'react-dom/client' // 开发环境调试面板 if (process.env.NODE_ENV === 'development') { const DebugPanel = () => { const [stats, setStats] = React.useState({}) React.useEffect(() => { const interval = setInterval(() => { setStats({ modules: window.__webpack_require__.c, loadTimes: window.moduleLoadTimes, remoteModules: window.remoteModules }) }, 1000) return () => clearInterval(interval) }, []) return React.createElement('div', { style: { position: 'fixed', bottom: 0, right: 0, background: 'rgba(0,0,0,0.8)', color: 'white', padding: '10px', fontSize: '12px', zIndex: 9999 } }, React.createElement('pre', null, JSON.stringify(stats, null, 2))) } const root = createRoot(document.createElement('div')) root.render(React.createElement(DebugPanel)) } ``` 通过以上调试工具和方法,可以有效地排查和解决 Module Federation 开发过程中的各种问题。
前端 · 2026年2月19日 17:46
Module Federation 如何支持多团队协作?有哪些协作最佳实践?Module Federation 的多团队协作模式需要建立清晰的规范和流程,以下是详细的协作方案: **1. 团队组织架构** **跨团队协作模型:** ``` ┌─────────────────────────────────────────────┐ │ 平台团队 (Platform Team) │ │ - Module Federation 基础设施 │ │ - 共享依赖管理 │ │ - CI/CD 管道 │ │ - 代码规范制定 │ └──────────────┬──────────────────────────────┘ │ ┌──────────┼──────────┬──────────┐ │ │ │ │ ┌───▼───┐ ┌───▼───┐ ┌───▼───┐ ┌───▼───┐ │订单团队│ │用户团队│ │支付团队│ │商品团队│ └───────┘ └───────┘ └───────┘ └───────┘ ``` **2. 代码规范和约定** **模块命名规范:** ```javascript // 命名约定:{team}-{module}-{type} // 例如:order-list-component, user-api-service // 模块配置示例 new ModuleFederationPlugin({ name: 'order-list-component', filename: 'remoteEntry.js', exposes: { './OrderList': './src/components/OrderList', './OrderListAPI': './src/api/orderList' }, shared: { react: { singleton: true }, 'react-dom': { singleton: true } } }) ``` **目录结构规范:** ``` packages/ ├── order-list-component/ │ ├── src/ │ │ ├── components/ │ │ │ ├── OrderList.tsx │ │ │ └── OrderItem.tsx │ │ ├── api/ │ │ │ └── orderList.ts │ │ ├── hooks/ │ │ │ └── useOrderList.ts │ │ ├── types/ │ │ │ └── order.ts │ │ └── index.ts │ ├── package.json │ ├── webpack.config.js │ └── README.md ├── user-profile-component/ │ └── ... └── shared-dependencies/ └── ... ``` **3. API 设计规范** **统一的 API 接口:** ```typescript // types/api.ts export interface ApiResponse<T> { data: T code: number message: string } export interface PaginationParams { page: number pageSize: number } export interface PaginatedResponse<T> extends ApiResponse<T[]> { total: number page: number pageSize: number } // 订单模块 API export interface OrderListParams extends PaginationParams { status?: string startDate?: string endDate?: string } export interface Order { id: string orderNo: string status: string amount: number createdAt: string } export const orderAPI = { getList: (params: OrderListParams): Promise<PaginatedResponse<Order>> => { return request.get('/orders', { params }) }, getDetail: (id: string): Promise<ApiResponse<Order>> => { return request.get(`/orders/${id}`) } } ``` **4. 版本管理策略** **语义化版本控制:** ```json { "name": "order-list-component", "version": "1.2.3", "description": "Order list component with filtering and pagination", "main": "index.js", "peerDependencies": { "react": "^17.0.0", "react-dom": "^17.0.0" }, "dependencies": { "@company/shared-types": "^1.0.0" } } ``` **版本兼容性矩阵:** ```javascript // version-matrix.js const versionMatrix = { 'order-list-component': { '1.x': { compatibleHost: ['>=1.0.0'], breakingChanges: [] }, '2.0.0': { compatibleHost: ['>=2.0.0'], breakingChanges: [ 'Removed deprecated API methods', 'Changed component props interface' ] } } } export function checkCompatibility(moduleName, moduleVersion, hostVersion) { const moduleInfo = versionMatrix[moduleName] if (!moduleInfo) return true const majorVersion = moduleVersion.split('.')[0] const versionInfo = moduleInfo[`${majorVersion}.x`] || moduleInfo[majorVersion] if (!versionInfo) return false return versionInfo.compatibleHost.some(range => semver.satisfies(hostVersion, range) ) } ``` **5. 文档规范** **模块文档模板:** ```markdown # Order List Component ## 概述 订单列表组件,支持筛选、分页、排序等功能。 ## 安装 \`\`\`bash npm install @company/order-list-component \`\`\` ## 使用方法 \`\`\`jsx import OrderList from '@company/order-list-component' function App() { return ( <OrderList status="pending" onOrderClick={(order) => console.log(order)} /> ) } \`\`\` ## API 文档 ### Props | 属性 | 类型 | 必填 | 默认值 | 说明 | |------|------|------|--------|------| | status | string | 否 | 'all' | 订单状态筛选 | | pageSize | number | 否 | 20 | 每页显示数量 | | onOrderClick | function | 否 | - | 订单点击回调 | ### Events | 事件名 | 参数 | 说明 | |--------|------|------| | orderClick | order: Order | 订单点击事件 | | pageChange | page: number | 页码变化事件 | ## 版本历史 - 1.2.3 (2024-01-15): 修复分页bug - 1.2.0 (2024-01-10): 新增排序功能 - 1.0.0 (2024-01-01): 初始版本 ## 贡献者 - 订单团队: order-team@company.com ``` **6. CI/CD 流程** **自动化构建和部署:** ```yaml # .github/workflows/build-and-deploy.yml name: Build and Deploy on: push: branches: [main, develop] pull_request: branches: [main] jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - name: Setup Node.js uses: actions/setup-node@v2 with: node-version: '16' - name: Install dependencies run: npm ci - name: Run linter run: npm run lint - name: Run tests run: npm test - name: Build run: npm run build - name: Upload artifacts uses: actions/upload-artifact@v2 with: name: dist path: dist/ deploy: needs: build runs-on: ubuntu-latest if: github.ref == 'refs/heads/main' steps: - name: Download artifacts uses: actions/download-artifact@v2 with: name: dist - name: Deploy to S3 uses: jakejarvis/s3-sync-action@master with: args: --delete env: AWS_S3_BUCKET: ${{ secrets.AWS_S3_BUCKET }} AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} SOURCE_DIR: 'dist' ``` **7. 代码审查流程** **Pull Request 模板:** ```markdown ## 变更类型 - [ ] 新功能 - [ ] Bug 修复 - [ ] 性能优化 - [ ] 文档更新 - [ ] 代码重构 ## 变更描述 简要描述本次变更的内容和目的。 ## 影响范围 - [ ] 仅影响当前模块 - [ ] 影响其他模块(请列出) - [ ] 影响主应用 ## 测试情况 - [ ] 单元测试已通过 - [ ] 集成测试已通过 - [ ] 手动测试已完成 ## 文档更新 - [ ] README 已更新 - [ ] API 文档已更新 - [ ] 变更日志已更新 ## 检查清单 - [ ] 代码符合团队规范 - [ ] 没有引入新的依赖(或已说明原因) - [ ] 版本号已更新(如有破坏性变更) - [ ] 向后兼容性已考虑 ``` **8. 依赖管理** **共享依赖管理:** ```javascript // packages/shared-dependencies/package.json { "name": "@company/shared-dependencies", "version": "1.0.0", "main": "index.js", "dependencies": { "react": "^17.0.2", "react-dom": "^17.0.2", "react-router-dom": "^6.0.0", "styled-components": "^5.3.0", "axios": "^0.27.0" } } // 使用共享依赖 new ModuleFederationPlugin({ name: 'order-list-component', shared: { ...require('@company/shared-dependencies') } }) ``` **9. 沟通机制** **团队沟通渠道:** - **Slack 频道**: #module-federation, #order-team, #user-team - **定期会议**: 每周技术同步会 - **文档中心**: Confluence 或 Notion - **问题追踪**: Jira 或 GitHub Issues **变更通知机制:** ```javascript // 变更通知服务 class ChangeNotificationService { constructor() { this.subscribers = new Map() } subscribe(moduleName, callback) { if (!this.subscribers.has(moduleName)) { this.subscribers.set(moduleName, []) } this.subscribers.get(moduleName).push(callback) } notify(moduleName, change) { const callbacks = this.subscribers.get(moduleName) || [] callbacks.forEach(callback => callback(change)) // 发送通知到 Slack this.sendSlackNotification(moduleName, change) } sendSlackNotification(moduleName, change) { const webhookUrl = process.env.SLACK_WEBHOOK_URL const message = { text: `Module ${moduleName} has been updated`, attachments: [{ color: 'good', fields: [ { title: 'Version', value: change.version, short: true }, { title: 'Type', value: change.type, short: true }, { title: 'Description', value: change.description, short: false } ] }] } fetch(webhookUrl, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(message) }) } } export const changeNotificationService = new ChangeNotificationService() ``` 通过以上协作模式,可以建立高效的 Module Federation 多团队协作体系。
前端 · 2026年2月19日 17:46
Module Federation 如何进行性能优化?有哪些最佳实践?Module Federation 的性能优化可以从多个维度进行,以下是详细的优化策略: **1. 构建产物优化** **代码分割:** ```javascript // Remote 应用配置 new ModuleFederationPlugin({ name: 'remoteApp', filename: 'remoteEntry.js', exposes: { './Button': './src/Button', './Modal': './src/Modal' }, // 细粒度暴露,避免打包不必要的内容 splitChunks: { chunks: 'all', cacheGroups: { vendor: { test: /[\\/]node_modules[\\/]/, name: 'vendors', priority: 10 } } } }) ``` **Tree Shaking:** - 确保使用 ES Module 语法 - 在 package.json 中设置 `"sideEffects": false` - 避免使用 CommonJS 的 require **2. 加载性能优化** **预加载关键模块:** ```javascript // 在应用空闲时预加载 const preloadRemoteModule = () => { if ('requestIdleCallback' in window) { requestIdleCallback(() => { import('remoteApp/HeavyComponent') }) } } // 或使用 link 标签预加载 const link = document.createElement('link') link.rel = 'preload' link.href = 'http://localhost:3001/remoteEntry.js' link.as = 'script' document.head.appendChild(link) ``` **CDN 部署:** - 将 remoteEntry.js 和模块文件部署到 CDN - 使用 HTTP/2 或 HTTP/3 加速传输 - 配置合理的缓存策略(Cache-Control: max-age=31536000) **3. 运行时优化** **共享依赖优化:** ```javascript shared: { react: { singleton: true, requiredVersion: deps.react, eager: false, // 避免急切加载 strictVersion: false // 允许版本不匹配 } } ``` **懒加载策略:** ```javascript // 使用 React.lazy 和 Suspense const RemoteComponent = React.lazy(() => import('remoteApp/Component') ) // 或使用动态导入 const loadModule = async () => { const module = await import('remoteApp/Module') return module.default } ``` **4. 缓存策略** **版本化文件名:** ```javascript // Webpack 配置 output: { filename: '[name].[contenthash].js', chunkFilename: '[name].[contenthash].js' } ``` **Service Worker 缓存:** ```javascript // 缓存 remoteEntry.js workbox.routing.registerRoute( /.*remoteEntry\.js/, new workbox.strategies.NetworkFirst({ cacheName: 'remote-entries', plugins: [ new workbox.expiration.ExpirationPlugin({ maxEntries: 10, maxAgeSeconds: 7 * 24 * 60 * 60 }) ] }) ) ``` **5. 监控和诊断** **性能监控:** ```javascript // 监控模块加载时间 const loadRemoteModule = async (moduleName) => { const startTime = performance.now() try { const module = await import(moduleName) const loadTime = performance.now() - startTime console.log(`${moduleName} loaded in ${loadTime}ms`) return module } catch (error) { console.error(`Failed to load ${moduleName}:`, error) throw error } } ``` **6. 最佳实践** - **按需暴露**:只暴露必要的模块,避免打包不相关的代码 - **版本控制**:使用语义化版本管理,确保兼容性 - **错误边界**:为远程模块添加错误边界,避免影响整个应用 - **降级方案**:准备本地降级组件,应对远程加载失败 - **渐进式加载**:优先加载核心功能,次要功能延迟加载 **性能指标:** - 首次内容绘制(FCP)< 1.8s - 最大内容绘制(LCP)< 2.5s - 累积布局偏移(CLS)< 0.1 - 首次输入延迟(FID)< 100ms 通过以上优化策略,可以显著提升 Module Federation 应用的性能表现。
前端 · 2026年2月19日 17:45
Module Federation 的安全性如何保障?有哪些安全最佳实践?Module Federation 的安全性是一个重要考虑因素,以下是主要的安全问题和防护措施: **1. 跨域资源共享(CORS)安全** **问题:** 远程模块加载涉及跨域请求,可能被恶意利用。 **解决方案:** ```javascript // 配置严格的 CORS 策略 devServer: { headers: { 'Access-Control-Allow-Origin': 'https://trusted-domain.com', 'Access-Control-Allow-Methods': 'GET', 'Access-Control-Allow-Headers': 'Content-Type', 'Access-Control-Max-Age': '86400' } } // 生产环境使用白名单 const allowedOrigins = [ 'https://app1.example.com', 'https://app2.example.com' ] devServer: { setupMiddlewares: (middlewares, devServer) => { devServer.app.use((req, res, next) => { const origin = req.headers.origin if (allowedOrigins.includes(origin)) { res.setHeader('Access-Control-Allow-Origin', origin) } next() }) return middlewares } } ``` **2. 内容安全策略(CSP)** **问题:** 动态加载的脚本可能违反 CSP 规则。 **解决方案:** ```html <!-- 配置 CSP 头 --> <meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self' https://trusted-cdn.com 'nonce-randomValue'; style-src 'self' 'unsafe-inline';"> <!-- 或在服务器配置 --> Content-Security-Policy: default-src 'self'; script-src 'self' https://cdn.example.com; object-src 'none' ``` **3. 远程模块验证** **问题:** 加载的远程模块可能被篡改或包含恶意代码。 **解决方案:** ```javascript // 使用 Subresource Integrity (SRI) const loadRemoteModule = async (url, integrity) => { const response = await fetch(url) const content = await response.text() // 验证内容完整性 const computedHash = await crypto.subtle.digest( 'SHA-256', new TextEncoder().encode(content) ) const computedIntegrity = 'sha256-' + btoa(String.fromCharCode(...new Uint8Array(computedHash))) if (computedIntegrity !== integrity) { throw new Error('Module integrity check failed') } // 动态执行模块 const blob = new Blob([content], { type: 'application/javascript' }) const moduleUrl = URL.createObjectURL(blob) return import(moduleUrl) } // 使用示例 loadRemoteModule( 'https://cdn.example.com/remoteEntry.js', 'sha256-abc123...' ) ``` **4. 依赖安全扫描** **问题:** 共享依赖可能包含已知漏洞。 **解决方案:** ```bash # 使用 npm audit 扫描依赖 npm audit # 使用 Snyk 进行深度扫描 npm install -g snyk snyk test # 在 CI/CD 中集成安全扫描 # .github/workflows/security.yml name: Security Scan on: [push, pull_request] jobs: security: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - name: Run npm audit run: npm audit --audit-level=moderate - name: Run Snyk uses: snyk/actions/node@master env: SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }} ``` **5. 权限控制** **问题:** 远程模块可能访问不应访问的资源。 **解决方案:** ```javascript // 使用沙箱隔离远程模块 class Sandbox { constructor() { this.context = Object.create(null) } execute(code) { const fn = new Function('context', ` with (context) { return (${code}) } `) return fn(this.context) } } // 限制可访问的全局对象 const sandbox = new Sandbox() sandbox.context.console = { log: (...args) => console.log('[Sandbox]', ...args) } // Proxy 拦截访问 const secureProxy = new Proxy(target, { get(target, prop) { if (allowedProps.includes(prop)) { return target[prop] } throw new Error(`Access to ${prop} is not allowed`) } }) ``` **6. HTTPS 和证书验证** **问题:** 中间人攻击可能导致远程模块被篡改。 **解决方案:** ```javascript // 强制使用 HTTPS const secureFetch = async (url) => { if (!url.startsWith('https://')) { throw new Error('Only HTTPS URLs are allowed') } const response = await fetch(url, { credentials: 'omit', mode: 'cors' }) if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`) } return response } // 配置证书固定(Certificate Pinning) // 注意:这需要在原生应用层实现 ``` **7. 版本锁定和依赖管理** **问题:** 依赖版本漂移可能导致安全漏洞。 **解决方案:** ```json // package-lock.json 确保依赖版本一致性 { "lockfileVersion": 2, "dependencies": { "react": { "version": "17.0.2", "integrity": "sha512-..." } } } // 使用 npm shrinkwrap 锁定依赖 npm shrinkwrap // 定期更新依赖并审计 npm update npm audit fix ``` **8. 监控和日志记录** **问题:** 难以追踪远程模块的加载和执行情况。 **解决方案:** ```javascript // 模块加载监控 const moduleLoadTracker = { track(moduleName, url, status, duration) { const event = { timestamp: Date.now(), moduleName, url, status, duration } // 发送到监控系统 if (navigator.sendBeacon) { navigator.sendBeacon('/api/module-tracking', JSON.stringify(event)) } } } // 使用示例 const loadWithTracking = async (moduleName, url) => { const startTime = performance.now() try { const module = await import(url) moduleLoadTracker.track(moduleName, url, 'success', performance.now() - startTime) return module } catch (error) { moduleLoadTracker.track(moduleName, url, 'error', performance.now() - startTime) throw error } } ``` **最佳实践总结:** 1. **使用 HTTPS**:所有远程模块必须通过 HTTPS 加载 2. **实施 CSP**:配置严格的内容安全策略 3. **验证完整性**:使用 SRI 验证远程模块的完整性 4. **定期审计**:定期扫描依赖漏洞 5. **最小权限原则**:限制远程模块的访问权限 6. **监控日志**:记录所有模块加载事件 7. **版本锁定**:使用 package-lock.json 锁定依赖版本 8. **白名单机制**:只允许受信任的域名加载远程模块 通过以上安全措施,可以有效降低 Module Federation 的安全风险。
前端 · 2026年2月19日 17:45
Module Federation 如何进行测试?有哪些测试策略?Module Federation 的测试策略需要考虑模块的独立性和依赖关系,以下是完整的测试方案: **1. 单元测试** **测试远程模块:** ```javascript // Button.test.js import { render, screen } from '@testing-library/react' import Button from './Button' describe('Button Component', () => { it('renders button with correct text', () => { render(<Button label="Click me" />) expect(screen.getByText('Click me')).toBeInTheDocument() }) it('calls onClick handler when clicked', () => { const handleClick = jest.fn() render(<Button label="Click me" onClick={handleClick} />) screen.getByText('Click me').click() expect(handleClick).toHaveBeenCalledTimes(1) }) }) ``` **测试共享依赖:** ```javascript // sharedDependency.test.js import React from 'react' describe('Shared React Dependency', () => { it('uses singleton instance', () => { const react1 = require('react') const react2 = require('react') expect(react1).toBe(react2) }) it('maintains correct version', () => { const react = require('react') expect(react.version).toMatch(/^17\./) }) }) ``` **2. 集成测试** **测试模块加载:** ```javascript // moduleLoading.test.js import { render, screen, waitFor } from '@testing-library/react' describe('Remote Module Loading', () => { it('loads remote component successfully', async () => { const RemoteComponent = React.lazy(() => import('remoteApp/Button') ) render( <React.Suspense fallback={<div>Loading...</div>}> <RemoteComponent label="Remote Button" /> </React.Suspense> ) await waitFor(() => { expect(screen.getByText('Remote Button')).toBeInTheDocument() }) }) it('handles loading errors gracefully', async () => { const FallbackComponent = () => <div>Fallback</div> const RemoteComponent = React.lazy(() => import('remoteApp/NonExistent') .catch(() => import('./Fallback')) ) render( <React.Suspense fallback={<div>Loading...</div>}> <RemoteComponent /> </React.Suspense> ) await waitFor(() => { expect(screen.getByText('Fallback')).toBeInTheDocument() }) }) }) ``` **测试模块间通信:** ```javascript // interModuleCommunication.test.js describe('Inter-Module Communication', () => { it('shares state between modules', async () => { const { createStore } = await import('remoteApp/store') const store = createStore() store.dispatch({ type: 'SET_VALUE', payload: 'test' }) expect(store.getState().value).toBe('test') }) it('emits events across modules', async () => { const { eventBus } = await import('remoteApp/eventBus') const handler = jest.fn() eventBus.on('test-event', handler) eventBus.emit('test-event', { data: 'test' }) expect(handler).toHaveBeenCalledWith({ data: 'test' }) }) }) ``` **3. 端到端测试** **使用 Cypress 测试:** ```javascript // cypress/integration/app.spec.js describe('Module Federation E2E', () => { it('loads remote module on page load', () => { cy.visit('/') cy.get('[data-testid="remote-button"]').should('be.visible') cy.get('[data-testid="remote-button"]').should('contain', 'Remote Button') }) it('interacts with remote component', () => { cy.visit('/') cy.get('[data-testid="remote-button"]').click() cy.get('[data-testid="message"]').should('contain', 'Button clicked') }) it('handles module loading failure', () => { cy.intercept('GET', '**/remoteEntry.js', { forceNetworkError: true }) cy.visit('/') cy.get('[data-testid="fallback-message"]').should('be.visible') }) }) ``` **使用 Playwright 测试:** ```javascript // app.spec.ts import { test, expect } from '@playwright/test' test.describe('Module Federation E2E', () => { test('loads remote module successfully', async ({ page }) => { await page.goto('/') const button = page.locator('[data-testid="remote-button"]') await expect(button).toBeVisible() await expect(button).toHaveText('Remote Button') }) test('handles remote module interactions', async ({ page }) => { await page.goto('/') await page.click('[data-testid="remote-button"]') const message = page.locator('[data-testid="message"]') await expect(message).toContainText('Button clicked') }) }) ``` **4. 性能测试** **模块加载性能测试:** ```javascript // performance.test.js describe('Module Loading Performance', () => { it('loads remote module within acceptable time', async () => { const startTime = performance.now() await import('remoteApp/Button') const loadTime = performance.now() - startTime expect(loadTime).toBeLessThan(2000) // 2秒内加载完成 }) it('measures bundle size', () => { const stats = require('./webpack-stats.json') const remoteEntrySize = stats.assets.find( asset => asset.name === 'remoteEntry.js' ).size expect(remoteEntrySize).toBeLessThan(100 * 1024) // 小于100KB }) }) ``` **5. Mock 远程模块** **使用 Jest Mock:** ```javascript // __mocks__/remoteApp/Button.js export default function MockButton({ label }) { return <button data-testid="mock-button">{label}</button> } // test file jest.mock('remoteApp/Button') import Button from 'remoteApp/Button' describe('Mocked Remote Module', () => { it('uses mocked component', () => { render(<Button label="Mocked Button" />) expect(screen.getByTestId('mock-button')).toBeInTheDocument() }) }) ``` **使用 MSW 拦截请求:** ```javascript // msw.js import { setupServer } from 'msw/node' import { rest } from 'msw' const server = setupServer( rest.get('http://localhost:3001/remoteEntry.js', (req, res, ctx) => { return res( ctx.set('Content-Type', 'application/javascript'), ctx.body(` var remoteApp; (function() { remoteApp = { get: function(module) { return import('./' + module + '.js'); }, init: function(sharedScope) { // initialization logic } }; })(); `) ) }) ) beforeAll(() => server.listen()) afterEach(() => server.resetHandlers()) afterAll(() => server.close()) ``` **6. 测试配置** **Jest 配置:** ```javascript // jest.config.js module.exports = { preset: 'ts-jest', testEnvironment: 'jsdom', moduleNameMapper: { '\\.(css|less|scss|sass)$': 'identity-obj-proxy', '^@/(.*)$': '<rootDir>/src/$1', '^remoteApp/(.*)$': '<rootDir>/__mocks__/remoteApp/$1' }, transform: { '^.+\\.(ts|tsx|js|jsx)$': 'ts-jest' }, setupFilesAfterEnv: ['<rootDir>/jest.setup.js'] } ``` **Cypress 配置:** ```javascript // cypress.config.js const { defineConfig } = require('cypress') module.exports = defineConfig({ e2e: { baseUrl: 'http://localhost:3000', supportFile: 'cypress/support/e2e.js', specPattern: 'cypress/integration/**/*.cy.{js,jsx,ts,tsx}', viewportWidth: 1280, viewportHeight: 720 } }) ``` **7. 测试最佳实践** 1. **隔离测试**:每个测试独立运行,不依赖其他测试 2. **Mock 外部依赖**:使用 Mock 隔离外部依赖 3. **覆盖边界情况**:测试成功、失败、超时等场景 4. **性能基准**:设定性能基准,监控性能退化 5. **持续集成**:在 CI/CD 中集成测试 6. **测试覆盖率**:保持高测试覆盖率(>80%) 7. **文档化**:为复杂测试添加注释和文档 通过以上测试策略,可以确保 Module Federation 应用的质量和稳定性。
前端 · 2026年2月19日 17:45
Module Federation 的未来发展趋势是什么?有哪些技术演进方向?Module Federation 的未来发展趋势和生态系统正在快速演进,以下是详细的分析和展望: **1. 技术发展趋势** **跨构建工具支持:** ```javascript // Vite + Module Federation // vite.config.js import federation from '@originjs/vite-plugin-federation' export default { plugins: [ federation({ name: 'vite-app', filename: 'remoteEntry.js', exposes: { './Button': './src/Button' }, shared: ['react', 'react-dom'] }) ] } // Rollup + Module Federation // rollup.config.js import federation from '@module-federation/rollup-plugin' export default { plugins: [ federation({ name: 'rollup-app', filename: 'remoteEntry.js', exposes: { './Button': './src/Button' }, shared: ['react', 'react-dom'] }) ] } // esbuild + Module Federation // esbuild.config.js import { federationPlugin } from '@module-federation/esbuild-plugin' export default { plugins: [ federationPlugin({ name: 'esbuild-app', filename: 'remoteEntry.js', exposes: { './Button': './src/Button' }, shared: ['react', 'react-dom'] }) ] } ``` **2. 性能优化方向** **智能预加载:** ```javascript // AI 驱动的预加载策略 class IntelligentPreloader { constructor() { this.userBehavior = new Map() this.loadPredictions = new Map() } trackUserBehavior(event) { // 跟踪用户行为 const timestamp = Date.now() const key = `${event.type}_${event.target}` if (!this.userBehavior.has(key)) { this.userBehavior.set(key, []) } this.userBehavior.get(key).push(timestamp) // 预测用户下一步操作 this.predictNextAction(event) } predictNextAction(event) { // 使用简单的机器学习模型预测 const patterns = this.analyzePatterns() const prediction = this.makePrediction(patterns, event) if (prediction.confidence > 0.7) { this.preloadModule(prediction.module) } } analyzePatterns() { const patterns = [] this.userBehavior.forEach((timestamps, key) => { if (timestamps.length > 1) { const avgInterval = this.calculateAverageInterval(timestamps) patterns.push({ key, avgInterval, frequency: timestamps.length }) } }) return patterns.sort((a, b) => b.frequency - a.frequency) } calculateAverageInterval(timestamps) { let total = 0 for (let i = 1; i < timestamps.length; i++) { total += timestamps[i] - timestamps[i - 1] } return total / (timestamps.length - 1) } makePrediction(patterns, currentEvent) { // 简化的预测逻辑 const likelyNext = patterns.find(p => p.key.startsWith(currentEvent.type) && p.frequency > 3 ) if (likelyNext) { return { module: this.mapEventToModule(likelyNext.key), confidence: Math.min(likelyNext.frequency / 10, 0.9) } } return { module: null, confidence: 0 } } mapEventToModule(eventKey) { const moduleMap = { 'click_dashboard': 'dashboardModule/Dashboard', 'click_orders': 'orderModule/OrderList', 'click_users': 'userModule/UserProfile' } return moduleMap[eventKey] || null } async preloadModule(moduleName) { if (!moduleName || this.loadPredictions.has(moduleName)) return try { await import(moduleName) this.loadPredictions.set(moduleName, true) console.log(`✅ Preloaded module: ${moduleName}`) } catch (error) { console.error(`❌ Failed to preload module: ${moduleName}`, error) } } } export const intelligentPreloader = new IntelligentPreloader() ``` **边缘计算集成:** ```javascript // 边缘节点模块分发 class EdgeModuleDistributor { constructor() { this.edgeNodes = new Map() this.moduleCache = new Map() } async registerEdgeNode(nodeId, location) { // 注册边缘节点 this.edgeNodes.set(nodeId, { location, modules: new Map(), lastSync: Date.now() }) console.log(`✅ Edge node registered: ${nodeId} at ${location}`) } async distributeModule(moduleName, moduleData) { // 分发模块到边缘节点 const distributionPromises = [] for (const [nodeId, node] of this.edgeNodes) { distributionPromises.push( this.deployToEdgeNode(nodeId, moduleName, moduleData) ) } await Promise.allSettled(distributionPromises) console.log(`✅ Module distributed to edge nodes: ${moduleName}`) } async deployToEdgeNode(nodeId, moduleName, moduleData) { // 部署到边缘节点 const node = this.edgeNodes.get(nodeId) try { const response = await fetch(`${node.location}/deploy`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ moduleName, moduleData }) }) if (response.ok) { node.modules.set(moduleName, { deployedAt: Date.now(), status: 'active' }) } } catch (error) { console.error(`Failed to deploy to edge node ${nodeId}:`, error) } } async loadFromNearestEdge(moduleName) { // 从最近的边缘节点加载模块 const nearestNode = this.findNearestEdgeNode() if (nearestNode && nearestNode.modules.has(moduleName)) { try { const response = await fetch( `${nearestNode.location}/modules/${moduleName}` ) const moduleData = await response.json() // 动态加载模块 const blob = new Blob([moduleData.code], { type: 'application/javascript' }) const moduleUrl = URL.createObjectURL(blob) const module = await import(moduleUrl) console.log(`✅ Loaded module from edge: ${moduleName}`) return module } catch (error) { console.error(`Failed to load from edge node:`, error) } } // 回退到 CDN return this.loadFromCDN(moduleName) } findNearestEdgeNode() { // 简化的最近节点查找 return Array.from(this.edgeNodes.values())[0] } async loadFromCDN(moduleName) { // 从 CDN 加载模块 return import(moduleName) } } export const edgeModuleDistributor = new EdgeModuleDistributor() ``` **3. 生态系统扩展** **模块市场:** ```javascript // Module Marketplace class ModuleMarketplace { constructor() { this.modules = new Map() this.categories = new Map() this.ratings = new Map() } async publishModule(moduleData) { // 发布模块到市场 const moduleId = this.generateModuleId(moduleData) const module = { id: moduleId, name: moduleData.name, version: moduleData.version, description: moduleData.description, author: moduleData.author, category: moduleData.category, remoteEntry: moduleData.remoteEntry, publishedAt: Date.now(), downloads: 0, rating: 0 } this.modules.set(moduleId, module) // 更新分类 this.updateCategory(moduleData.category, moduleId) console.log(`✅ Module published: ${module.name}`) return module } async searchModules(query, filters = {}) { // 搜索模块 let results = Array.from(this.modules.values()) // 关键词搜索 if (query) { const lowerQuery = query.toLowerCase() results = results.filter(module => module.name.toLowerCase().includes(lowerQuery) || module.description.toLowerCase().includes(lowerQuery) ) } // 分类过滤 if (filters.category) { results = results.filter(module => module.category === filters.category ) } // 评分过滤 if (filters.minRating) { results = results.filter(module => module.rating >= filters.minRating ) } return results } async installModule(moduleId) { // 安装模块 const module = this.modules.get(moduleId) if (!module) { throw new Error(`Module not found: ${moduleId}`) } try { // 动态加载远程模块 const remoteModule = await import(module.remoteEntry) // 增加下载计数 module.downloads++ console.log(`✅ Module installed: ${module.name}`) return remoteModule } catch (error) { console.error(`Failed to install module:`, error) throw error } } async rateModule(moduleId, rating, review) { // 评价模块 const module = this.modules.get(moduleId) if (!module) { throw new Error(`Module not found: ${moduleId}`) } const ratingData = { userId: this.getCurrentUserId(), rating, review, timestamp: Date.now() } if (!this.ratings.has(moduleId)) { this.ratings.set(moduleId, []) } this.ratings.get(moduleId).push(ratingData) // 更新平均评分 const ratings = this.ratings.get(moduleId) module.rating = ratings.reduce((sum, r) => sum + r.rating, 0) / ratings.length console.log(`✅ Module rated: ${module.name} (${rating}/5)`) return module } generateModuleId(moduleData) { return `${moduleData.name}-${moduleData.version}-${Date.now()}` } updateCategory(category, moduleId) { if (!this.categories.has(category)) { this.categories.set(category, new Set()) } this.categories.get(category).add(moduleId) } getCurrentUserId() { // 获取当前用户 ID return 'user-' + Math.random().toString(36).substr(2, 9) } } export const moduleMarketplace = new ModuleMarketplace() ``` **4. 安全性增强** **区块链验证:** ```javascript // Blockchain-based Module Verification class BlockchainVerifier { constructor() { this.blockchain = new Map() this.currentBlock = 0 } async registerModule(moduleData) { // 在区块链上注册模块 const block = { index: this.currentBlock++, timestamp: Date.now(), moduleHash: this.calculateHash(moduleData), moduleData: moduleData, previousHash: this.getPreviousHash() } block.hash = this.calculateBlockHash(block) this.blockchain.set(block.index, block) console.log(`✅ Module registered on blockchain: ${moduleData.name}`) return block } async verifyModule(moduleName, moduleData) { // 验证模块完整性 const block = this.findModuleBlock(moduleName) if (!block) { throw new Error(`Module not found on blockchain: ${moduleName}`) } const currentHash = this.calculateHash(moduleData) if (currentHash !== block.moduleHash) { throw new Error(`Module integrity verification failed: ${moduleName}`) } // 验证区块链完整性 const isValid = this.verifyBlockchain() if (!isValid) { throw new Error('Blockchain verification failed') } console.log(`✅ Module verified: ${moduleName}`) return true } calculateHash(data) { // 计算数据哈希 const crypto = require('crypto') return crypto .createHash('sha256') .update(JSON.stringify(data)) .digest('hex') } calculateBlockHash(block) { const crypto = require('crypto') return crypto .createHash('sha256') .update( block.index + block.previousHash + block.timestamp + block.moduleHash ) .digest('hex') } getPreviousHash() { if (this.currentBlock === 0) { return '0' } return this.blockchain.get(this.currentBlock - 1).hash } findModuleBlock(moduleName) { for (const block of this.blockchain.values()) { if (block.moduleData.name === moduleName) { return block } } return null } verifyBlockchain() { // 验证区块链完整性 for (let i = 1; i < this.currentBlock; i++) { const currentBlock = this.blockchain.get(i) const previousBlock = this.blockchain.get(i - 1) if (currentBlock.previousHash !== previousBlock.hash) { return false } const calculatedHash = this.calculateBlockHash(currentBlock) if (calculatedHash !== currentBlock.hash) { return false } } return true } } export const blockchainVerifier = new BlockchainVerifier() ``` **5. 开发者工具** **可视化调试器:** ```javascript // Visual Module Federation Debugger class VisualDebugger { constructor() { this.moduleGraph = new Map() this.loadTimes = new Map() this.dependencies = new Map() } visualizeModuleGraph() { // 可视化模块依赖图 const graphData = this.buildGraphData() return { nodes: graphData.nodes, edges: graphData.edges, layout: this.calculateLayout(graphData) } } buildGraphData() { const nodes = [] const edges = [] this.moduleGraph.forEach((module, id) => { nodes.push({ id, label: module.name, type: module.type, status: module.status }) module.dependencies.forEach(depId => { edges.push({ source: id, target: depId, type: 'dependency' }) }) }) return { nodes, edges } } calculateLayout(graphData) { // 简化的布局计算 const positions = new Map() graphData.nodes.forEach((node, index) => { positions.set(node.id, { x: (index % 5) * 200, y: Math.floor(index / 5) * 150 }) }) return positions } generatePerformanceReport() { // 生成性能报告 const report = { totalModules: this.moduleGraph.size, averageLoadTime: this.calculateAverageLoadTime(), slowModules: this.identifySlowModules(), dependencyChains: this.analyzeDependencyChains(), recommendations: this.generateRecommendations() } return report } calculateAverageLoadTime() { const times = Array.from(this.loadTimes.values()) return times.reduce((sum, time) => sum + time, 0) / times.length } identifySlowModules() { const threshold = 1000 // 1秒 const slowModules = [] this.loadTimes.forEach((time, moduleId) => { if (time > threshold) { slowModules.push({ moduleId, loadTime: time, module: this.moduleGraph.get(moduleId) }) } }) return slowModules.sort((a, b) => b.loadTime - a.loadTime) } analyzeDependencyChains() { // 分析依赖链 const chains = [] this.moduleGraph.forEach((module, id) => { if (module.dependencies.length > 0) { chains.push({ root: id, depth: this.calculateDepth(id), branches: module.dependencies.length }) } }) return chains } calculateDepth(moduleId) { const module = this.moduleGraph.get(moduleId) if (!module || module.dependencies.length === 0) { return 0 } return 1 + Math.max( ...module.dependencies.map(dep => this.calculateDepth(dep)) ) } generateRecommendations() { const recommendations = [] // 基于慢模块生成建议 const slowModules = this.identifySlowModules() if (slowModules.length > 0) { recommendations.push({ type: 'performance', priority: 'high', message: `${slowModules.length} modules load slowly. Consider code splitting or lazy loading.` }) } // 基于依赖链生成建议 const deepChains = this.analyzeDependencyChains().filter(c => c.depth > 5) if (deepChains.length > 0) { recommendations.push({ type: 'architecture', priority: 'medium', message: `${deepChains.length} modules have deep dependency chains. Consider flattening dependencies.` }) } return recommendations } } export const visualDebugger = new VisualDebugger() ``` 通过以上技术趋势和生态系统的分析,可以看出 Module Federation 的未来发展将更加注重性能、安全性和开发者体验。
前端 · 2026年2月19日 17:45
Module Federation 如何与 React、Vue、Angular 等框架配合使用?Module Federation 可以与多种前端框架配合使用,以下是各框架的具体实现方案: **1. React + Module Federation** **基础配置:** ```javascript // webpack.config.js const { ModuleFederationPlugin } = require('webpack').container module.exports = { // ...其他配置 plugins: [ new ModuleFederationPlugin({ name: 'reactApp', filename: 'remoteEntry.js', exposes: { './Button': './src/Button', './Header': './src/Header' }, shared: { react: { singleton: true, eager: true }, 'react-dom': { singleton: true, eager: true } } }) ] } ``` **使用远程组件:** ```javascript import React, { Suspense } from 'react' const RemoteButton = React.lazy(() => import('remoteApp/Button')) function App() { return ( <Suspense fallback={<div>Loading...</div>}> <RemoteButton label="Click me" /> </Suspense> ) } ``` **2. Vue 3 + Module Federation** **基础配置:** ```javascript // vue.config.js const { defineConfig } = require('@vue/cli-service') const ModuleFederationPlugin = require('webpack').container.ModuleFederationPlugin module.exports = defineConfig({ configureWebpack: { plugins: [ new ModuleFederationPlugin({ name: 'vueApp', filename: 'remoteEntry.js', exposes: { './Button': './src/components/Button.vue' }, shared: { vue: { singleton: true, eager: true } } }) ] } }) ``` **使用远程组件:** ```javascript <template> <div> <Suspense> <template #default> <RemoteButton :text="buttonText" /> </template> <template #fallback> <div>Loading...</div> </template> </Suspense> </div> </template> <script> import { defineAsyncComponent } from 'vue' export default { components: { RemoteButton: defineAsyncComponent(() => import('remoteApp/Button') ) }, data() { return { buttonText: 'Click me' } } } </script> ``` **3. Angular + Module Federation** **基础配置:** ```javascript // webpack.config.js const ModuleFederationPlugin = require('webpack').container.ModuleFederationPlugin module.exports = { plugins: [ new ModuleFederationPlugin({ name: 'angularApp', filename: 'remoteEntry.js', exposes: { './ButtonComponent': './src/app/button/button.component.ts' }, shared: { '@angular/core': { singleton: true }, '@angular/common': { singleton: true } } }) ] } ``` **使用远程组件:** ```typescript import { Component, NgModule } from '@angular/core' import { BrowserModule } from '@angular/platform-browser' @Component({ selector: 'app-root', template: ` <ng-container *ngIf="remoteButton"> <ng-container *ngComponentOutlet="remoteButton"></ng-container> </ng-container> ` }) export class AppComponent { remoteButton: any async ngOnInit() { const module = await import('remoteApp/ButtonComponent') this.remoteButton = module.ButtonComponent } } ``` **4. Svelte + Module Federation** **基础配置:** ```javascript // rollup.config.js import moduleFederation from '@module-federation/rollup-plugin' export default { plugins: [ moduleFederation({ name: 'svelteApp', filename: 'remoteEntry.js', exposes: { './Button': './src/Button.svelte' }, shared: { svelte: { singleton: true } } }) ] } ``` **使用远程组件:** ```javascript <script> import { onMount } from 'svelte' let RemoteButton onMount(async () => { const module = await import('remoteApp/Button') RemoteButton = module.default }) </script> {#if RemoteButton} <svelte:component this={RemoteButton} text="Click me" /> {/if} ``` **5. 框架间互操作** **React 使用 Vue 组件:** ```javascript import React from 'react' const VueComponentWrapper = React.lazy(() => import('vueApp/Button') ) function ReactApp() { return ( <Suspense fallback={<div>Loading Vue component...</div>}> <VueComponentWrapper /> </Suspense> ) } ``` **Vue 使用 React 组件:** ```javascript <template> <div> <ReactComponentWrapper /> </div> </template> <script> import { defineAsyncComponent, onMounted } from 'vue' export default { components: { ReactComponentWrapper: defineAsyncComponent(() => import('reactApp/Button') ) } } </script> ``` **最佳实践:** 1. **统一依赖版本**:确保所有应用使用相同版本的共享依赖 2. **类型安全**:使用 TypeScript 或类型声明文件确保类型安全 3. **错误处理**:为远程组件添加错误边界和降级方案 4. **性能优化**:使用懒加载和代码分割优化性能 5. **样式隔离**:使用 CSS Modules 或 CSS-in-JS 避免样式冲突 通过以上配置,Module Federation 可以与各种前端框架无缝集成,实现跨框架的模块共享。
前端 · 2026年2月19日 17:45
Module Federation 和 qiankun、single-spa 有什么区别?Module Federation 和传统的微前端方案(如 qiankun、single-spa)有以下主要区别: **1. 构建时机不同** - **Module Federation**:在构建时生成独立的构建产物,运行时动态加载 - **qiankun/single-spa**:需要构建完整的子应用,主应用通过 JS/CSS 加载子应用 **2. 依赖共享机制** - **Module Federation**:原生支持依赖共享,通过 shared 配置自动管理版本冲突 - **qiankun**:需要手动配置 importMap 或使用沙箱隔离依赖 - **single-spa**:依赖 SystemJS 模块加载器,需要额外配置依赖管理 **3. 代码隔离方式** - **Module Federation**:通过 Webpack 的模块系统实现隔离,共享依赖时使用单例模式 - **qiankun**:使用 JS 沙箱(快照沙箱或代理沙箱)隔离全局变量 - **single-spa**:依赖 SystemJS 的模块隔离机制 **4. 开发体验** - **Module Federation**:配置相对简单,与 Webpack 深度集成,支持 HMR - **qiankun**:需要适配子应用生命周期,配置相对复杂 - **single-spa**:学习曲线较陡,需要理解 SystemJS 和生命周期 **5. 性能对比** - **Module Federation**:按需加载模块,共享依赖减少重复代码,性能更优 - **qiankun**:需要加载完整的子应用,可能存在代码重复 - **single-spa**:通过 SystemJS 加载模块,有一定的运行时开销 **6. 技术栈限制** - **Module Federation**:主要支持 Webpack 5,对其他构建工具支持有限 - **qiankun**:不限制技术栈,支持 Vue、React、Angular 等多种框架 - **single-spa**:框架无关,支持任意前端技术栈 **7. 适用场景** - **Module Federation**:适合同技术栈、需要细粒度模块共享的场景 - **qiankun**:适合异构技术栈、需要完整应用隔离的场景 - **single-spa**:适合需要高度定制化微前端架构的场景
前端 · 2026年2月19日 17:44