1. 项目背景与核心需求
在大型前端项目中,我们经常会遇到这样的场景:一个系统由多个独立功能模块组成,这些模块可能由不同团队开发、使用不同技术栈、有独立的发布周期。传统单体应用架构下,这些模块耦合在一起,导致:
- 技术栈升级困难(比如从Vue2迁移到Vue3)
- 构建部署效率低下(任何小改动都需要全量发布)
- 团队协作成本高(代码冲突频繁)
这正是我们需要微前端架构的场景。qiankun作为阿里开源的微前端解决方案,完美解决了这些问题。我最近在一个金融项目中实践了qiankun架构,主应用和子应用都基于Vue2(若依框架),下面分享具体实现方案。
2. 技术选型分析
2.1 为什么选择qiankun
对比市面上主流微前端方案,qiankun具有明显优势:
| 方案 | 技术栈限制 | 样式隔离 | JS沙箱 | 通信机制 | 接入复杂度 |
|---|---|---|---|---|---|
| iframe | 无 | 完美 | 完美 | postMessage | 低 |
| single-spa | 无 | 需手动 | 需手动 | 自定义 | 高 |
| Module Federation | 需Webpack5 | 部分 | 无 | 共享依赖 | 中 |
| qiankun | 无 | 自动 | 自动 | 内置机制 | 低 |
实际项目中我们发现iframe的体验问题(路由同步、全局遮罩等)难以接受,而qiankun的HTML Entry设计让子应用接入就像iframe一样简单。
2.2 Vue2作为主应用的技术考量
选择Vue2(若依框架)作为主应用主要基于:
- 团队技术栈统一(现有系统都是Vue2)
- 若依框架内置权限管理、多标签页等企业级功能
- Vue2的稳定性和社区支持度
3. 具体实现方案
3.1 主应用配置
// main/src/micro-fe.js import { registerMicroApps, start } from 'qiankun' registerMicroApps([ { name: 'subapp1', entry: '//localhost:7101', container: '#subapp-container', activeRule: '/subapp1', props: { token: localStorage.getItem('token'), onGlobalStateChange: (state, prev) => { console.log('主应用收到状态变更:', state) } } } ]) // 启动时预加载子应用资源 start({ prefetch: true })关键配置说明:
entry: 子应用访问地址,开发环境用本地服务,生产环境用CDN地址activeRule: 路由匹配规则,支持字符串和函数两种形式props: 向子应用传递的全局状态和方法
3.2 子应用改造
子应用需要导出生命周期钩子:
// subapp1/src/public-path.js if (window.__POWERED_BY_QIANKUN__) { __webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__ } // subapp1/src/main.js import './public-path' let instance = null function render(props = {}) { const { container } = props instance = new Vue({ router, store, render: h => h(App) }).$mount(container ? container.querySelector('#app') : '#app') } // 独立运行时 if (!window.__POWERED_BY_QIANKUN__) { render() } export async function bootstrap() { console.log('[vue] vue app bootstraped') } export async function mount(props) { console.log('[vue] props from main framework', props) render(props) } export async function unmount() { instance.$destroy() instance.$el.innerHTML = '' instance = null }3.3 样式隔离方案
qiankun默认会为子应用添加样式隔离,但我们在实践中发现两种特殊情况需要处理:
- 全局样式污染:主应用的reset.css会影响子应用
/* 主应用中添加作用域 */ .app-main { /* 主应用样式 */ } /* 子应用中使用唯一前缀 */ .subapp1-container { /* 子应用样式 */ }- 动态加载样式丢失:子应用异步加载的组件样式可能失效 解决方案是在webpack配置中添加:
// vue.config.js css: { extract: false // 禁用CSS提取 }4. 核心问题解决方案
4.1 路由冲突问题
当主应用和子应用都使用vue-router时,需要特殊处理:
// 子应用router配置 const router = new VueRouter({ mode: 'history', base: window.__POWERED_BY_QIANKUN__ ? '/subapp1/' : '/', routes })同时主应用需要配置路由通配:
// 主应用router.js { path: '/subapp1*', meta: { requiresAuth: false } }4.2 状态管理方案
我们采用qiankun自带的initGlobalState机制:
// 主应用初始化状态 import { initGlobalState } from 'qiankun' const actions = initGlobalState({ user: { name: 'admin' }, token: 'xxxx' }) // 子应用监听状态变化 export function mount(props) { props.onGlobalStateChange((state, prev) => { console.log('子应用收到状态:', state) }) // 子应用也可以更新状态 props.setGlobalState({ token: 'newToken' }) }4.3 公共依赖处理
通过webpack externals共享公共库:
// 子应用vue.config.js configureWebpack: { externals: { 'vue': 'Vue', 'vue-router': 'VueRouter', 'axios': 'axios' } } // 主应用index.html <script src="//cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.runtime.min.js"></script> <script src="//cdn.jsdelivr.net/npm/vue-router@3.5.3/dist/vue-router.min.js"></script>5. 性能优化实践
5.1 资源预加载
start({ prefetch: 'all' // 可选值: 'all' | string[] | (apps) => apps })5.2 子应用缓存
通过自定义loader实现子应用静态资源缓存:
import { loadMicroApp } from 'qiankun' const cachedApps = {} function loadApp(name, config) { if (cachedApps[name]) { return cachedApps[name] } const app = loadMicroApp(config) cachedApps[name] = app return app }5.3 按需加载策略
// 动态注册子应用 function registerDynamicApp(path) { const appConfig = await fetchAppConfig(path) registerMicroApps([appConfig]) }6. 常见问题排查
6.1 子应用加载失败
现象:控制台报错"Application died in status LOADING_SOURCE_CODE"
排查步骤:
- 检查子应用入口HTML是否能正常访问
- 确认子应用打包配置正确(publicPath设置)
- 检查跨域配置(开发环境需要设置headers)
6.2 样式丢失
解决方案:
- 检查子应用是否使用了scoped样式
- 确认webpack的css-loader配置正确
- 尝试设置
singular: false禁用严格样式隔离
6.3 路由跳转异常
典型场景:子应用内部跳转导致页面刷新
修复方案:
// 子应用main.js router.afterEach(() => { if (window.__POWERED_BY_QIANKUN__) { const base = router.options.base window.history.pushState(null, '', base + to.fullPath) } })7. 项目实战经验
7.1 开发环境配置技巧
推荐使用vue-cli的devServer配置代理:
// vue.config.js devServer: { port: 7101, headers: { 'Access-Control-Allow-Origin': '*' } }7.2 生产环境部署方案
最佳实践是主应用和子应用分开部署:
https://main-app.com # 主应用 https://sub-app1.com # 子应用1 https://sub-app2.com # 子应用2通过Nginx配置路由转发:
location /subapp1 { proxy_pass https://sub-app1.com; }7.3 监控与错误处理
在主应用中统一捕获子应用错误:
import { addErrorHandler } from 'qiankun' addErrorHandler(error => { console.error('微应用错误:', error) Sentry.captureException(error) })8. 升级与迁移策略
8.1 Vue2到Vue3的渐进迁移
利用qiankun可以实现渐进式升级:
- 先保持主应用为Vue2
- 新功能用Vue3开发为独立子应用
- 逐步将旧模块重构成Vue3子应用
8.2 从单体应用到微前端
迁移路线图:
- 先抽取公共依赖(Vue、VueRouter等)
- 按功能模块拆分路由
- 将每个路由对应模块改造成子应用
- 最后拆解主应用的业务逻辑
9. 安全最佳实践
9.1 子应用沙箱加固
start({ sandbox: { strictStyleIsolation: true, // 严格的样式隔离 experimentalStyleIsolation: true // 实验性的样式隔离 } })9.2 权限控制方案
主应用统一控制子应用访问权限:
registerMicroApps(apps.map(app => ({ ...app, activeRule: location => { return hasPermission(app.name) && location.pathname.startsWith(app.activeRule) } })))10. 项目成果与展望
通过qiankun微前端改造,我们的项目获得了:
- 构建时间从15分钟降到3分钟(各子应用并行构建)
- 团队协作效率提升40%(各团队独立开发部署)
- 系统稳定性提升(错误隔离)
未来计划:
- 尝试接入React子应用(验证多框架支持)
- 实现子应用热更新(不刷新主应用)
- 探索Serverless部署方案