【架构实战】微前端架构设计与落地
一、微前端概述
微前端(Micro Frontend)是将微服务思想应用到前端的一种架构模式:
核心理念:
- 将大型前端应用拆分为独立可部署的小应用
- 各子应用可以独立开发、测试、部署
- 技术栈无关,打破团队边界
解决的问题:
- 大型前端项目的维护困难
- 多团队协作的代码冲突
- 技术栈升级的困难
- 巨石应用的性能问题
二、微前端架构模式
1. 架构图
┌─────────────────────────────────────────────────────────────────┐ │ 微前端架构 │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ │ │ 会员子应用 │ │ 订单子应用 │ │ 商品子应用 │ │ │ │ (React) │ │ (Vue) │ │ (Angular) │ │ │ └──────┬───────┘ └──────┬───────┘ └──────┬───────┘ │ │ │ │ │ │ │ ┌──────▼──────────────────▼──────────────────▼───────┐ │ │ │ 容器应用 │ │ │ │ (主应用骨架、路由管理、公共组件、样式隔离) │ │ │ └────────────────────────┬───────────────────────────┘ │ │ │ │ │ ┌────────────────────────▼───────────────────────────┐ │ │ │ 独立部署平台 │ │ │ │ (CI/CD流水线、版本管理、灰度发布) │ │ │ └────────────────────────────────────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────┘2. 技术选型对比
| 方案 | 描述 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|---|
| iframe | 嵌套独立页面 | 隔离性强 | 体验差、通讯难 | 简单场景 |
| JS Bundle | 动态加载子应用 | 体验好 | 样式冲突 | 中等复杂度 |
| Web Component | 自定义组件 | 原生隔离 | 学习曲线 | 新项目 |
| Module Federation | Webpack联邦模块 | 成熟、共享依赖 | 需Webpack | 已有项目改造 |
3. Module Federation方案
主应用配置:
// 主应用 webpack.config.jsconstModuleFederationPlugin=require('webpack/lib/container/ModuleFederationPlugin');constdeps=require('./package.json').dependencies;module.exports={mode:'development',devServer:{port:3000,},plugins:[newModuleFederationPlugin({name:'container',// 共享的依赖(子应用可以复用)shared:{react:{singleton:true,requiredVersion:deps.react},'react-dom':{singleton:true,requiredVersion:deps['react-dom']}}})]};子应用配置:
// 子应用(Vue)webpack.config.jsmodule.exports={mode:'development',devServer:{port:3001,// 暴露子应用headers:{'Access-Control-Allow-Origin':'*'}},plugins:[newModuleFederationPlugin({name:'orderApp',// 暴露的模块exposes:{'./OrderModule':'./src/OrderModule'},// 共享依赖shared:{vue:{singleton:true},'vue-router':{singleton:true}}})]};三、微前端框架对比
1. qiankun(阿里)
特点:
- 基于single-spa
- 接入简单,API友好
- 样式隔离
- 资源预加载
主应用:
// main.jsimport{registerMicroApps,start}from'qiankun';constapps=[{name:'orderApp',// 子应用名称entry:'//localhost:3001',// 子应用入口container:'#container',// 挂载容器activeRule:'/order'// 激活路由},{name:'productApp',entry:'//localhost:3002',container:'#container',activeRule:'/product'}];// 注册子应用registerMicroApps(apps);// 启动start();// 设置全局生命周期import{setGlobalUnmount}from'qiankun';setGlobalUnmount(()=>{console.log('子应用卸载');});子应用(Vue):
// src/main.jsletvueApp=null;functionrender(props={}){const{container}=props;vueApp=createApp(App);vueApp.use(router);vueApp.mount(container?container.querySelector('#app'):'#app');}// qiankun生命周期if(process.env.NODE_ENV==='development'){render();}exportasyncfunctionbootstrap(){console.log('子应用启动');}exportasyncfunctionmount(props){console.log('子应用挂载',props);render(props);}exportasyncfunctionunmount(){console.log('子应用卸载');vueApp.$destroy();vueApp.$el.innerHTML='';}2. EMP(欢聚时代)
特点:
- 基于Webpack Module Federation
- 支持多实例
- 共享依赖更高效
3. Garfish(字节)
特点:
- 支持多实例隔离
- 支持预渲染
- 完善的沙箱机制
四、微前端通信方案
1. Props通信
// 主应用传递数据<MicroApp name="orderApp"data={{userId:123}}/>// 子应用接收functionApp(props){const{data}=props;console.log('收到主应用数据:',data.userId);}2. 自定义事件
// 发送事件(子应用)import{emit}from'qiankun';emit('user-login',{userId:123,token:'xxx'});// 监听事件(另一个子应用)import{onGlobalStateChange}from'qiankun';onGlobalStateChange((state)=>{console.log('收到全局状态变更:',state);});3. 共享状态(Redux/Vuex)
// shared/store/index.jsimport{createStore}from'redux';constinitialState={user:null,token:null};functionreducer(state=initialState,action){switch(action.type){case'SET_USER':return{...state,user:action.payload};default:returnstate;}}exportconststore=createStore(reducer);// 主应用初始化import{initGlobalState}from'qiankun';constactions=initGlobalState({user:null,token:null});// 监听变化actions.onGlobalStateChange((state)=>{console.log('全局状态变化:',state);store.dispatch({type:'SET_USER',payload:state.user});});4. 基于事件总线的通信
// eventBus.jsclassEventBus{constructor(){this.events={};}on(event,callback){if(!this.events[event]){this.events[event]=[];}this.events[event].push(callback);}emit(event,data){if(this.events[event]){this.events[event].forEach(cb=>cb(data));}}off(event,callback){if(this.events[event]){this.events[event]=this.events[event].filter(cb=>cb!==callback);}}}exportconsteventBus=newEventBus();五、样式隔离方案
1. CSS Modules
/* 子应用样式 */.container{padding:20px;}.title{color:blue;}/* 生成唯一的类名 */.container{color:blue;}.container__title{color:blue;}2. CSS-in-JS
// 使用styled-componentsimportstyledfrom'styled-components';constContainer=styled.div`padding: 20px;`;constTitle=styled.h1`color: blue; font-size: 24px;`;// 样式自动隔离3. Shadow DOM
// 使用Web ComponentclassOrderWidgetextendsHTMLElement{constructor(){super();constshadow=this.attachShadow({mode:'closed'});shadow.innerHTML=`<style> .container { padding: 20px; } </style> <div class="container"> <h1>订单应用</h1> </div>`;}}customElements.define('order-widget',OrderWidget);4. 样式前缀隔离
// 为子应用添加唯一前缀functionmountWithPrefix(container,prefix){conststyleElements=container.querySelectorAll('style');styleElements.forEach(style=>{constcss=style.textContent;// 为所有选择器添加前缀constprefixedCSS=addPrefix(css,`.${prefix}`);style.textContent=prefixedCSS;});}六、子应用加载与路由
1. 路由分发
// 主应用路由import{createRouter,createWebHistory}from'vue-router';constroutes=[{path:'/',component:Home},// 动态加载子应用{path:'/order/:page*',component:()=>import('./OrderApp.vue')},{path:'/product/:page*',component:()=>import('./ProductApp.vue')}];constrouter=createRouter({history:createWebHistory(),routes});2. 预加载
// 主应用预加载子应用import{preloadApp}from'qiankun';preloadApp([{name:'orderApp',entry:'//localhost:3001'}]);3. 懒加载
// Vue Router懒加载子应用constroutes=[{path:'/order',component:()=>import('./views/Order.vue')}];// React Router懒加载constOrderApp=lazy(()=>import('orderApp/OrderModule'));七、微前端最佳实践
1. 项目结构
├── micro-frontend-project/ │ ├── container/ # 主应用 │ │ ├── src/ │ │ ├── public/ │ │ ├── package.json │ │ └── webpack.config.js │ │ │ ├── apps/ │ │ ├── order-app/ # 订单子应用 │ │ │ ├── src/ │ │ │ └── package.json │ │ │ │ │ ├── product-app/ # 商品子应用 │ │ │ ├── src/ │ │ │ └── package.json │ │ │ │ │ └── user-app/ # 用户子应用 │ │ ├── src/ │ │ └── package.json │ │ │ └── shared/ # 共享代码 │ ├── components/ │ ├── utils/ │ └── styles/2. 开发规范
// 1. 子应用导出标准接口export{bootstrap,mount,unmount};// 2. 统一状态管理// 每个子应用使用独立状态,但通过事件与主应用通信// 3. 统一错误处理window.addEventListener('error',(e)=>{console.error('子应用错误:',e.error);});// 4. 统一日志console.log('[order-app]','用户操作日志');3. 性能优化
// 1. 预加载关键子应用import{prefetchApps}from'qiankun';prefetchApps([{name:'orderApp',entry:'//localhost:3001'}]);// 2. 代码分割constOrderModule=lazy(()=>import('./OrderModule'));// 3. 缓存子应用资源// 配置CDN缓存策略4. 安全考虑
// 1. 子应用资源校验constALLOWED_APPS=['orderApp','productApp','userApp'];functionvalidateApp(appName,appEntry){if(!ALLOWED_APPS.includes(appName)){thrownewError('未授权的子应用');}}// 2. CSP配置// 限制子应用可以加载的资源八、常见问题与解决方案
1. 样式冲突
/* 方案1:CSS命名空间 */.order-app .container{}.product-app .container{}/* 方案2:CSS Modules */:local(.container){}/* 方案3:Shadow DOM */2. 状态共享
// 共享用户信息constsharedState={user:null,permissions:[]};// 主应用设置functionsetUser(user){sharedState.user=user;emitGlobalStateChange(sharedState);}// 子应用获取onGlobalStateChange((state)=>{sharedState.user=state.user;});3. 公共依赖
// webpack配置共享依赖shared:{react:{singleton:true,eager:true},'react-dom':{singleton:true,eager:true}}4. 子应用通信
// 使用qiankun的initGlobalStateimport{initGlobalState}from'qiankun';constinitialState={};constactions=initGlobalState(initialState);// 主应用监听actions.onGlobalStateChange((state)=>{console.log('状态变化:',state);});// 子应用修改actions.setGlobalState({token:'xxx'});九、总结
微前端是大型前端项目的解决方案:
- 独立开发:各子应用可独立开发
- 独立部署:解除团队耦合
- 技术多样:支持不同技术栈
- 渐进迁移:逐步改造旧项目
最佳实践:
- 优先使用qiankun或Module Federation
- 做好样式隔离
- 设计好通信机制
- 统一开发规范
个人观点,仅供参考
