Module Federation
Module Federation 是 Webpack 5 引入的一项革命性功能,旨在实现不同前端应用之间的模块共享和动态加载,从而促进微前端架构的发展。它允许多个独立构建的应用在运行时共享代码和依赖,无需预先打包到单一应用中,极大地降低了代码冗余和版本冲突的风险。通过 Module Federation,应用可以暴露自己的模块供其他应用动态加载,同时也能远程加载其他应用暴露的模块,实现跨应用的资源复用。其核心优势包括支持异步加载、版本兼容管理和独立部署,极大提升了大型项目的灵活性和扩展性。开发者可以通过简单配置,指定哪些模块需要共享,哪些模块是远程加载,配合 Webpack 的构建流程无缝集成。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