把 Vue2 项目“黑盒”嵌进 Vue3:qiankun 微前端实战笔记
🧑💻 关于我
三年全栈实践 · Vue + Java
真实项目沉淀,可复用代码与踩坑解法👋 关注我,收获实战经验,少走弯路
📬 绿泡泡:
it全栈小屋🔗 代码获取:https://gitee.com/xiaohelikesleep
🔗 CSDN:https://blog.csdn.net/interest_ing_
🔗 掘金:https://juejin.cn/user/3556246962047559
🔗 博客:https://blog.hfh.asia
把 Vue2 项目“黑盒”嵌进 Vue3:qiankun 微前端实战笔记
30分钟无痛迁移,不改一行老代码
引子:一个让人头大的需求
最近接到一个看似简单实则棘手的需求:把已经上线的 Vue2 老项目,整合到 Vue3 新项目中。
老项目运行稳定,但源码已经“尘封”,没人敢动;新项目要用 Vue3 技术栈,不能为了迁就老项目降低版本。
更麻烦的是:
- 老项目有独立的路由、状态管理、UI 组件库
- 两个项目技术栈不同,构建工具也不同(Webpack vs Vite)
- 要求双向通信:主应用能传参给子应用,子应用也能触发主应用的事件
一句话总结:把 Vue2 项目当一个“黑盒”,塞进 Vue3 项目里。
选型:为什么是 qiankun?
先看看市面上的方案:
| 方案 | 是否改源码 | 样式隔离 | 重复打包 | 体验 |
|---|---|---|---|---|
| iframe | ❌ 不改 | ✅ 天然隔离 | ❌ 不重复 | 宽高难调、刷新丢失状态、通信麻烦 |
| npm 包 | ✅ 必须改 | ❌ 需额外处理 | ✅ 重复打包 | 改到怀疑人生 |
| qiankun | ❌ 不改 | ✅ 自动隔离 | ❌ 独立加载 | 真香 |
qiankun 的核心优势:
- 零改造:子应用只需导出生命周期函数,业务代码一行不动
- 样式隔离:内置沙箱,不用担心 Vue2 和 Vue3 样式互相污染
- 按需加载:访问对应路由时才加载子应用资源
- 通信机制:官方提供 props 传参和全局状态管理
整体架构
┌─────────────────────────────────────────────────────┐
│ Vue3 主应用 (qiankun) │
│ ┌─────────────────────────────────────────────┐ │
│ │ 路由: /vue2 → 加载子应用 │ │
│ │ props: { userName, userId, onLogin } │ │
│ │ container: #subapp-view │ │
│ └─────────────────────────────────────────────┘ │
│ ↓ │
│ qiankun 加载 & 通信 │
│ ↓ │
│ ┌─────────────────────────────────────────────┐ │
│ │ Vue2 子应用 (独立部署) │ │
│ │ - 接收 props 数据 │ │
│ │ - 回调主应用方法 │ │
│ │ - 业务代码一行不改 │ │
│ └─────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────┘
第一步:改造子应用(Vue2)
只需添加 3 个文件改动,业务代码完全不碰。
1. 新增 public-path.js
解决子应用资源加载路径问题:
// public-path.js
if (window.__POWERED_BY_QIANKUN__) {
__webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
}
2. 改造 main.js
导出 qiankun 需要的生命周期函数:
import './public-path'
import Vue from 'vue'
import App from './App.vue'
import router from './router'
let instance = null
function render(props = {}) {
const { container } = props
instance = new Vue({
router,
render: h => h(App)
}).$mount(container ? container.querySelector('#app') : '#app')
}
// 独立运行时直接启动
if (!window.__POWERED_BY_QIANKUN__) {
render()
}
// 微前端生命周期(必须导出)
export async function bootstrap() {
console.log('Vue2 子应用启动')
}
export async function mount(props) {
// 把 props 挂到全局,方便业务组件使用
Vue.prototype.$mainProps = props
render(props)
}
export async function unmount() {
instance.$destroy()
instance = null
}
3. 配置 vue.config.js
关键配置:UMD 打包格式 + 跨域头。
const { defineConfig } = require('@vue/cli-service')
module.exports = defineConfig({
publicPath: '/vue2-child/', // 子应用部署路径
outputDir: 'dist',
configureWebpack: {
output: {
library: 'vue2App', // 必须与入口 entry 匹配
libraryTarget: 'umd', // 必须是 umd
}
},
devServer: {
port: 7100,
headers: {
'Access-Control-Allow-Origin': '*' // 必须允许跨域
}
}
})
⚠️ 注意:
library名称要与主应用注册时name一致,否则加载会失败。
打完包,扔到 nginx 即可独立部署。
第二步:主应用(Vue3)接入
1. 安装依赖
npm i qiankun
2. 注册子应用
创建 src/qiankun/index.js:
import { registerMicroApps, start } from 'qiankun'
registerMicroApps([
{
name: 'vue2App', // 与子应用 library 名称一致
entry: '//localhost:7100', // 子应用地址(开发环境)
container: '#subapp-view', // 挂载 DOM 节点
activeRule: '/vue2', // 路由匹配规则
props: { // 向下传递的数据和方法
userName: 'Alice',
userId: 123,
onLogin: (data) => {
console.log('子应用登录了', data)
// 可以在这里触发主应用的状态更新
}
}
}
])
// 启动 qiankun,开启样式隔离
start({
sandbox: {
strictStyleIsolation: true
}
})
3. 添加挂载点
在 App.vue 或对应布局中添加:
<template>
<div id="app">
<nav>
<router-link to="/">首页</router-link>
<router-link to="/vue2">进入老系统</router-link>
</nav>
<!-- 子应用挂载点 -->
<div id="subapp-view" />
</div>
</template>
4. 配置路由
在路由配置中添加对应路径(或者使用通配符):
const routes = [
// ... 其他路由
{
path: '/vue2',
name: 'Vue2App',
component: { render: () => null }, // 占位组件
meta: { title: '老系统' }
},
// 子应用内部路由也需要主应用识别
{
path: '/vue2/:pathMatch(.*)*',
component: { render: () => null }
}
]
访问 /vue2 时,qiankun 会自动拉取子应用资源并渲染到 #subapp-view 中。
第三步:双向通信
主应用 → 子应用(数据下发)
注册时通过 props 下发的数据,子应用在 mount 阶段接收:
// 子应用 main.js
export async function mount(props) {
// 挂载到 Vue 原型,全局可用
Vue.prototype.$mainProps = props
render(props)
}
// 子应用任意组件中使用
export default {
computed: {
userName() {
return this.$mainProps?.userName || '未登录'
}
}
}
子应用 → 主应用(事件回调)
主应用通过 props 传递回调函数,子应用按需调用:
// 子应用登录组件
export default {
methods: {
handleLogin() {
// 调用主应用传入的回调
this.$mainProps?.onLogin?.({
token: 'abc123',
userId: this.$mainProps?.userId
})
}
}
}
全局状态通信(跨层级)
qiankun 提供了 initGlobalState 实现跨应用全局状态:
// 主应用
import { initGlobalState } from 'qiankun'
const actions = initGlobalState({
count: 0,
theme: 'light'
})
// 监听变化
actions.onGlobalStateChange((newState, oldState) => {
console.log('主应用收到状态变化', newState)
})
// 子应用
export async function mount(props) {
// 监听全局状态
props.onGlobalStateChange((newState, oldState) => {
console.log('子应用收到', newState)
})
// 更新全局状态
props.setGlobalState({ count: 1 })
}
第四步:打包部署
子应用部署
子应用 dist 目录部署到 nginx 静态目录:
# nginx 配置
server {
listen 80;
server_name static.example.com;
location /vue2-child/ {
alias /path/to/vue2-app/dist/;
try_files $uri $uri/ /vue2-child/index.html;
# 必须配置跨域
add_header Access-Control-Allow-Origin *;
add_header Access-Control-Allow-Methods "GET, POST, OPTIONS";
}
}
主应用部署
主应用 nginx 添加反向代理(解决跨域):
server {
listen 80;
server_name main.example.com;
location / {
root /path/to/vue3-app/dist/;
try_files $uri $uri/ /index.html;
}
# 代理子应用静态资源
location /vue2-child/ {
proxy_pass http://static.example.com/vue2-child/;
proxy_set_header Host $host;
add_header Access-Control-Allow-Origin *;
}
}
生产环境配置
修改主应用注册时的 entry:
registerMicroApps([
{
// ...
entry: '//your-domain.com/vue2-child/', // 生产地址
// ...
}
])
常见问题 & 解决方案
1. 子应用资源 404
原因:publicPath 未正确配置
解决:确保 public-path.js 在 main.js 最顶部引入,且 webpack 配置了正确的 publicPath
2. 样式互相污染
原因:没有开启沙箱隔离
解决:启动时配置 strictStyleIsolation: true
start({
sandbox: { strictStyleIsolation: true }
})
3. 子应用路由跳转失败
原因:history 模式与主应用 basename 不匹配
解决:子应用路由配置 base 与 activeRule 保持一致
// 子应用 router/index.js
const router = new VueRouter({
mode: 'history',
base: '/vue2', // 与主应用 activeRule 一致
routes
})
4. 开发环境热更新失效
原因:webpack5 默认限制了允许访问的主机
解决:配置 devServer.allowedHosts
devServer: {
allowedHosts: 'all', // 或指定域名
// ...
}
5. 子应用重复渲染
原因:unmount 生命周期未正确销毁实例
解决:确保 unmount 中调用 instance.$destroy() 并置 null
总结
通过 qiankun,我们成功实现了:
✅ 零业务代码改动:Vue2 老项目原封不动
✅ 技术栈无关:Vue3 主应用 + Vue2 子应用和谐共存
✅ 双向通信:父子应用数据互通、事件联动
✅ 样式隔离:不用担心样式冲突
✅ 按需加载:访问时才加载,不影响首屏性能
整个过程 30 分钟完成,老项目和新项目各自独立迭代,互不干扰。
微前端不是银弹,但对于“遗留系统收编”这种场景,qiankun 确实是最优解。
如果你也在做微前端迁移,或者遇到其他技术难题,欢迎在评论区交流讨论!
📬 更多实战干货
三年全栈经验持续输出
可复用代码 · 踩坑记录 · 成长心得👋 全平台关注,一起成长
📬 绿泡泡:it全栈小屋
🔗 代码获取:https://gitee.com/xiaohelikesleep
🔗 CSDN:https://blog.csdn.net/interest_ing_
🔗 掘金:https://juejin.cn/user/3556246962047559
🔗 博客:https://blog.hfh.asia
用代码记录成长,用分享对抗遗忘
感谢阅读,我们下篇见 👋
更多推荐



所有评论(0)