在上一篇文章《微前端入门:qiankun + Vue 3 + Vite 从0搭建第一个微前端应用》中,我们已经成功跑通了第一个 qiankun 微前端应用。本文深入解析 qiankun 三个核心概念,帮你理解背后的工作原理,解决日常开发 80% 的问题。
一、路由联动原理
qiankun 的路由联动机制其实很简单:主应用负责根据 URL 匹配激活哪个微应用,微应用负责自己内部的路由跳转。
工作流程
sequenceDiagram
participant U as User
participant M as Main App
participant Q as qiankun
participant A as Micro App
U->>M: 访问 /app1/about
M->>Q: 路由变化
Q->>Q: 匹配 activeRule:路径以 /app1 开头 → 匹配微应用 app1
alt app1 not loaded
Q->>A: 加载资源 → 执行 bootstrap → mount
else app1 already mounted
Q->>A: 已经挂载 → 什么都不做
end
A->>A: 自己处理 /about 子路由
关键点
-
activeRule 匹配规则:
activeRule: '/app1'表示 URL 路径以/app1开头就激活这个微应用。所以微应用内部所有路由都以/app1开头。 -
基路径动态设置:微应用的路由基路径需要根据运行环境动态设置:
history: createWebHistory(qiankunWindow.__POWERED_BY_QIANKUN__ ? '/app1' : '/')
- 在 qiankun 环境下:基路径是
/app1,和 activeRule 一致 - 独立运行时:基路径是
/,方便开发调试
-
为什么都要用 history 模式?
- hash 模式也可以,但 history 模式地址栏更好看
- 双方都用 history 模式,地址栏可以正确同步
- 刷新页面后,路由可以正确匹配到微应用内部
-
刷新页面 404 问题:
- 开发环境:Vite 开发服务器已经默认支持 history 模式 fallback
- 生产环境:需要 Nginx 配置
try_files $uri $uri/ /index.html;把所有请求 fallback 到 index.html - 详细部署配置看第三篇文章
二、样式隔离深度解析
样式隔离是微前端最头疼的问题:主应用的样式不小心影响到微应用,或者反过来。qiankun 提供了三种隔离方案,各有优缺点:
方案对比
| 隔离方案 | 原理 | 优点 | 缺点 |
|---|---|---|---|
strictStyleIsolation: true | 使用 Shadow DOM 把微应用样式隔离起来 | 真正隔离,干净彻底 | 第三方组件(如弹窗、下拉)有时候无法正确挂载到 Shadow DOM 内部,显示不出来 |
experimentalStyleIsolation: true | 自动给微应用所有样式添加选择器前缀 | 兼容性好,第三方组件可以正常工作 | 隔离不是 100% 彻底,但满足绝大多数场景 |
| 不开启隔离 | 靠开发者手动命名空间 | 完全可控 | 对开发规范要求高,容易一不小心就污染了 |
推荐实践
双重保障法:
// 主应用启动配置
start({
sandbox: {
strictStyleIsolation: false, // 关闭 shadow DOM
experimentalStyleIsolation: true // 开启自动前缀
}
})
然后微应用所有内容用唯一根类名包裹:
<template>
<div class="micro-app-vue1">
<!-- 所有内容 -->
</div>
</template>
<style>
.micro-app-vue1 {
/* 所有样式写在这里 */
}
</style>
这样既能保证兼容性,又能大概率避免样式冲突。
解决第三方组件样式污染
如果使用第三方 UI 组件库(比如 Element Plus),样式还是会全局生效。这其实不是问题:
- 主应用和微应用使用同一个 UI 库,样式统一反而更好
- 如果版本不同,可以通过 UI 库的前缀选项给类名加前缀
三、跨应用通信方案
qiankun 内置了简单好用的全局状态管理,可以方便实现主应用 ↔ 微应用、微应用 ↔ 微应用之间的通信。
基本用法
主应用初始化状态
// 主应用 main.js
import { initGlobalState } from 'qiankun'
// 初始化全局状态
const actions = initGlobalState({
user: {
id: 1,
name: 'admin'
}
})
// 主应用修改全局状态
actions.setGlobalState({
user: { id: 2, name: 'guest' }
})
微应用监听并修改
// 微应用 main.js
let unsubscribe = null
renderWithQiankun({
mount(props) {
// 监听全局状态变化
unsubscribe = props.onGlobalStateChange((state, prev) => {
console.log('用户变化', state.user)
// 在这里更新你的应用状态
})
// 微应用修改全局状态
props.setGlobalState({ theme: 'dark' })
},
unmount() {
// 必须:卸载时取消监听,防止内存泄漏
unsubscribe?.()
app?.unmount()
app = null
}
})
什么时候需要自定义通信?
如果你的通信比较简单(用户信息、主题切换、菜单收起展开),qiankun 内置的 initGlobalState 足够用了。
如果需要更复杂的通信,可以用自定义事件或者浏览器 Broadcast Channel API。但大多数场景不需要。
通信最佳实践
- 单向数据流:尽量保持数据从主应用流向微应用,微应用修改后通知主应用
- 及时取消监听:
unmount生命周期一定要取消监听,否则会内存泄漏 - 不要过度通信:如果一个数据只在一个微应用里用,就不要放到全局状态
四、生命周期完整解读
qiankun 微应用有三个核心生命周期:bootstrap → mount → unmount。
| 生命周期 | 调用时机 | 应该做什么 |
|---|---|---|
bootstrap | 微应用第一次加载前调用 | 只调用一次,做一些全局初始化,比如加载字典数据 |
mount | 微应用激活需要挂载时调用,每次激活都会调用 | 渲染应用,创建路由,绑定事件监听 |
unmount | 微应用切换走后调用 | 销毁实例,清理定时器,移除事件监听,取消全局状态监听 → 防止内存泄漏 |
stateDiagram-v2
[*] --> bootstrap: 首次加载
bootstrap --> mount: 激活
mount --> unmount: 切换走
unmount --> mount: 再次激活
常见问题
Q: 为什么要每次 mount 都重新创建路由?
A: 如果路由只创建一次,存在变量里,上次的状态会残留到下次挂载。每次重新创建可以保证每次都是干净的状态,避免奇怪的 bug。虽然多一点创建开销,但完全可以忽略。
Q: unmount 必须做哪些清理工作?
A:
- 销毁 Vue 实例:
app?.unmount() - 置空实例引用:
app = null - 取消全局状态监听:调用
unsubscribe - 清理定时器:
clearInterval(timerId) - 移除全局事件监听:
window.removeEventListener
FAQ
Q: 样式隔离不开启可以吗?
A: 如果你的项目只有一个团队维护,大家都遵守命名规范,可以不开启。但还是推荐开启 experimentalStyleIsolation + 根类名,双重保障更安全。
Q: 多个微应用同时挂载在页面上可以吗?
A: 可以。qiankun 支持多个微应用同时存在,你只需要给每个微应用配置不同的 container 容器和不同的 activeRule 就行。
Q: 路由激活后,微应用没有重新渲染怎么办?
A: 检查 activeRule 是否匹配正确,URL 前缀是否和 activeRule 一致。还要检查 unmount 是否正确执行了。
Q: 主应用可以直接调用微应用的方法吗?
A: 不推荐。通过 onGlobalStateChange / setGlobalState 通信更解耦。主应用不应该知道微应用内部的具体方法。
本章小结
掌握了这三个核心概念,你就能解决 qiankun 开发中 80% 的问题:
- 路由联动: 主应用匹配前缀激活,微应用处理自己的子路由,基路径动态设置
- 样式隔离:
experimentalStyleIsolation+ 根类名命名空间,兼容性最好 - 跨应用通信: 内置
initGlobalState足够用,记得unmount取消监听 - 生命周期:
bootstrap一次,每次mount/unmount要做正确的清理
下一篇我们进入生产环境环节:打包优化、部署方案、常见坑点汇总,解决你上线会遇到的各种问题。