微前端独立部署实现应用独立发布与升级前言各位前端小伙伴在微前端架构中独立部署是一个非常重要的特性。想象一下当你有多个子应用时如果每次更新都需要重新部署整个应用那将是一场噩梦我曾经在一个单体应用中因为一个小小的bug修复不得不重新部署整个应用导致用户体验受到影响。后来我们引入了微前端独立部署每个子应用可以独立发布再也不用为了一个小改动而影响全局了独立部署架构架构图┌─────────────────────────────────────────────────────────────┐ │ CDN/DNS │ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │ │ Host │ │ App1 │ │ App2 │ │ Shared │ │ │ │ static │ │ static │ │ static │ │ static │ │ │ └────┬─────┘ └────┬─────┘ └────┬─────┘ └────┬─────┘ │ └───────┼─────────────┼─────────────┼─────────────┼────────┘ │ │ │ │ ▼ ▼ ▼ ▼ ┌─────────────────────────────────────────────────────────────┐ │ Load Balancer │ └─────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────┐ │ Web Server │ │ ┌──────────────────────────────────────────────────────┐ │ │ │ index.html (Host) │ │ │ │ - remoteEntry.js (App1) → http://cdn/app1/ │ │ │ │ - remoteEntry.js (App2) → http://cdn/app2/ │ │ │ └──────────────────────────────────────────────────────┘ │ └─────────────────────────────────────────────────────────────┘部署策略1. 基于CDN的部署// webpack.config.js module.exports { output: { filename: [name].[contenthash].js, publicPath: https://cdn.example.com/app1/ }, plugins: [ new ModuleFederationPlugin({ name: app1, filename: remoteEntry.js, exposes: { ./Button: ./src/components/Button } }) ] }2. 版本控制// Host配置 - 支持多版本 new ModuleFederationPlugin({ name: host, remotes: { app1: app1https://cdn.example.com/app1/v1/remoteEntry.js, app1_v2: app1https://cdn.example.com/app1/v2/remoteEntry.js } })3. 动态加载// 动态加载Remote async function loadRemote(remoteName, url) { return new Promise((resolve) { const script document.createElement(script) script.src url script.onload () { const proxy {} Object.keys(window[remoteName]).forEach(key { proxy[key] () window[remoteName].get(key) }) resolve(proxy) } document.head.appendChild(script) }) } // 使用 const app1 await loadRemote(app1, https://cdn.example.com/app1/remoteEntry.js) const Button await app1.Button()CI/CD流程GitHub Actions配置name: Deploy App1 on: push: branches: [ main ] paths: [ apps/app1/** ] jobs: deploy: runs-on: ubuntu-latest steps: - uses: actions/checkoutv4 - name: Use Node.js uses: actions/setup-nodev4 with: node-version: 20 - name: Install dependencies run: npm ci working-directory: ./apps/app1 - name: Build run: npm run build working-directory: ./apps/app1 - name: Deploy to CDN uses: jakejarvis/s3-sync-actionmaster with: args: --acl public-read --follow-symlinks 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: ./apps/app1/dist DEST_DIR: app1GitLab CI配置stages: - build - deploy build-app1: stage: build image: node:20 script: - cd apps/app1 - npm ci - npm run build artifacts: paths: - apps/app1/dist/ deploy-app1: stage: deploy image: amazon/aws-cli dependencies: - build-app1 script: - aws s3 sync apps/app1/dist/ s3://my-cdn/app1/ --acl public-read版本管理使用Semantic Versioning{ name: app1, version: 1.2.3, dependencies: { react: ^18.0.0, react-dom: ^18.0.0 } }版本路由// 根据用户特征选择版本 function getApp1Version() { const userVersion getUserVersion() if (userVersion beta) { return https://cdn.example.com/app1/v2/remoteEntry.js } return https://cdn.example.com/app1/v1/remoteEntry.js }回滚策略基于内容hash的回滚// 记录部署历史 const deployHistory [ { version: 1.0.0, hash: abc123, date: 2024-01-01 }, { version: 1.0.1, hash: def456, date: 2024-01-02 }, { version: 1.0.2, hash: ghi789, date: 2024-01-03 } ] // 回滚到指定版本 function rollback(version) { const deploy deployHistory.find(d d.version version) if (deploy) { updateRemoteEntry(deploy.hash) } }蓝绿部署// 蓝绿部署配置 const config { activeVersion: blue, versions: { blue: https://cdn.example.com/app1/blue/remoteEntry.js, green: https://cdn.example.com/app1/green/remoteEntry.js } } // 切换版本 function switchVersion() { config.activeVersion config.activeVersion blue ? green : blue reloadApp() }监控与告警部署监控// 监控部署状态 class DeploymentMonitor { constructor() { this.healthCheckInterval null } startMonitoring(url) { this.healthCheckInterval setInterval(async () { try { const response await fetch(${url}/health) if (!response.ok) { this.triggerAlert(Deployment failed) } } catch (error) { this.triggerAlert(Deployment error) } }, 5000) } stopMonitoring() { if (this.healthCheckInterval) { clearInterval(this.healthCheckInterval) } } triggerAlert(message) { // 发送告警通知 console.error(ALERT: ${message}) } }性能优化缓存策略// nginx配置 location /app1/ { expires 1y; add_header Cache-Control public, immutable; try_files $uri $uri/ 404; } location /app1/remoteEntry.js { expires 10m; add_header Cache-Control public; }预加载!-- 预加载关键资源 -- link relprefetch hrefhttps://cdn.example.com/app1/remoteEntry.js asscript / link relprefetch hrefhttps://cdn.example.com/app1/Button.js asscript /安全考虑CORS配置// server.js app.use((req, res, next) { res.setHeader(Access-Control-Allow-Origin, https://example.com) res.setHeader(Access-Control-Allow-Methods, GET, POST, OPTIONS) res.setHeader(Access-Control-Allow-Headers, Content-Type) next() })签名URL// 生成签名URL function generateSignedUrl(url, expiresIn 3600) { const expires Math.floor(Date.now() / 1000) expiresIn const signature sign(${url}${expires}, process.env.SECRET_KEY) return ${url}?expires${expires}signature${signature} }最佳实践1. 独立仓库每个子应用应该有独立的代码仓库便于独立开发和部署。2. 自动化部署使用CI/CD工具实现自动化部署减少人为错误。3. 版本追踪记录每次部署的版本信息便于回滚和审计。4. 健康检查部署后进行健康检查确保应用正常运行。5. 灰度发布先向部分用户发布新版本验证无误后再全量发布。总结独立部署是微前端架构的核心优势之一。通过独立部署我们可以独立发布每个子应用可以独立更新不影响其他应用快速迭代小团队可以快速发布功能无需等待其他团队风险隔离单个应用的问题不会影响整个系统版本控制支持多版本并行运行便于A/B测试现在开始规划你的微前端部署架构吧你会发现独立部署可以大大提高团队的开发效率最后一句忠告独立部署不是银弹需要配套的监控和回滚机制