微前端框架对比与实践:无界(Wujie)与乾坤(qiankun)
微前端框架对比与实践:无界(Wujie)与乾坤(qiankun)
目录
- 一、微前端基础概念
- 二、无界(Wujie)概览
- 三、无界在当前项目中的实践
- 四、乾坤(qiankun)概览
- 五、使用 qiankun 的示例(类比当前项目)
- 六、无界 vs 乾坤:对比与选型
- 七、学习与实践建议
一、微前端基础概念
微前端(Micro Frontends) 是借鉴微服务思想在前端领域的落地方案,其核心理念是:
- 将大型前端应用拆分成多个可以独立开发、独立部署的前端子应用
- 最终在浏览器端由一个 主应用(壳应用) 把多个子应用 聚合与调度
典型架构:
- 主应用
- 负责整体布局、框架、菜单、统一登录、权限控制
- 决定何时加载/卸载哪些子应用
- 子应用
- 各自维护自己的技术栈(Vue、React、Angular、纯 JS 等)
- 独立路由、独立部署、独立构建
微前端关注的核心问题:
- 样式隔离:避免子应用之间 CSS 相互污染
- JS 沙箱:控制子应用对
window等全局变量的污染 - 路由与加载策略:基于路由或条件决定激活哪个子应用
- 主子应用通信:共享用户信息、权限、全局事件等
无界(Wujie)和乾坤(qiankun)都是为了解决上述问题的微前端框架,但实现思路和使用方式有所差异。
二、无界(Wujie)概览
2.1 核心特点
无界(Wujie)是一个现代化的微前端框架,特点包括:
- 技术栈友好:对主应用/子应用技术栈不做强约束
- 接入简单:对子应用改造成本较低,很多情况下只需简单改入口
- 隔离性强:
- 通过
iframe + Web Components组合方式实现强隔离 - 样式和 JS 污染风险更小
- 通过
- 应用保活(keep-alive):
- 子应用切走后不销毁,再次切回来可快速恢复原状态
- 事件总线通信:
- 提供全局
bus,主/子应用之间可以事件形式通信
- 提供全局
- 多种插件能力:
- 可挂载 polyfill、资源处理、样式处理等插件
2.2 技术实现思路
核心实现思路可以概括为:
- 使用 Web Components + iframe 组合,将子应用“装进”一个隔离环境中
- 通过 JS 沙箱 + DOM 代理控制子应用能访问的全局对象
- 使用一套统一的容器组件(如
WujieVue)承载子应用的渲染 - 提供 事件总线 与 props 传参,实现主子应用之间的数据和行为通信
三、无界在当前项目中的实践
以下内容是基于你本地项目在
D:\newCode\newhub\product\view-decoration-pc下的实际使用方式总结,帮助你对照理解。
3.1 依赖与整体架构
项目中安装的无界相关依赖(package.json):
wujie-vue3:无界的 Vue3 集成包wujie-polyfill:无界的 polyfill,提供一些必要的浏览器兼容性支持
整体架构大致是:
- 主应用:Vue 3 + Vite,负责路由、菜单、权限等
- 子应用:CRM(
view-crm-pc)、会员(view-member-pc)等,通过无界挂载进来 - 微前端入口配置集中在一个
wujie.ts中
3.2 微应用配置与注册
典型的无界配置思路(与项目中 wujie.ts 类似):
// 示例:微应用列表配置
const microAppList = [
{
key: 'crm',
name: 'view-crm-pc', // 子应用名称(无界内部识别用)
url: import.meta.env.VITE_APP_SUB_V1, // 子应用部署地址
},
{
key: 'member',
name: 'view-member-pc',
url: import.meta.env.VITE_APP_SUB_V2,
},
]
const props = {
jump: (route) => {
// 主应用内的路由跳转能力传给子应用
router.push(route)
},
}
function setupWujie() {
microAppList.forEach(app => {
setupApp({
name: app.name,
url: app.url,
exec: true, // 是否执行 JS
alive: true, // 应用保活
props, // 传给子应用的公共 props
degrade, // 降级模式(兼容无 Proxy / CustomElementRegistry 场景)
plugins: [
LocationReloadPlugin,
{
patchElementHook: (element, iframeWindow) => {
// 针对 STYLE 标签的特殊处理,保证样式注入正确
},
},
],
})
})
}
主入口 main.ts 中的集成方式类似:
import WujieVue from 'wujie-vue3'
import setupWujie from './wujie'
const app = createApp(App)
// ... 其他 app.use(...)
app.use(WujieVue)
app.mount('#app')
// 启动微应用配置
setupWujie()
3.3 微应用容器组件
无界在 Vue3 中一般通过一个容器组件来渲染子应用,你项目里有类似的 MicroAppContainer:
- 根据路由
meta.microApp决定当前应该挂载哪个子应用 - 动态计算子应用 URL、props
- 控制加载状态(loading)、错误处理、生命周期日志等
典型写法示意:
<template>
<div class="micro-container" v-loading="loading">
<WujieVue
v-if="microApp"
width="100%"
height="100%"
:name="microApp.name"
:url="subAppUrl"
:props="subAppProps"
:fetch="credentialsFetch"
:beforeLoad="beforeLoad"
:afterMount="afterMount"
:activated="activated"
:deactivated="deactivated"
@loadError="handleLoadError"
/>
</div>
</template>
关键点:
subAppUrl:通常基于微应用基地址 + 当前路由拼接,保证子应用能感知路径subAppProps:把主应用中的 权限、菜单、用户信息 等通过 props 传给子应用credentialsFetch:统一封装fetch,自动携带token、Org-Id、Login-User-Id等头信息,子应用请求后端时自动完成鉴权- 生命周期钩子:
beforeLoad:开始加载,设置 loading = true,向子应用广播初始化 propsafterMount:加载完成,loading = falseactivated/deactivated:配合应用保活,在激活/切换时同步状态
3.4 主子应用通信
项目中通过无界的 全局事件总线 bus 做通信:
-
在一个专门的模块中(类似
micro-app.ts)集中处理:- 订阅全局消息,如:
global:message - 处理子应用发起的路由跳转,如:
sub-route-change - 处理子应用触发的登录过期事件,如:
micro-app:session-expired
- 订阅全局消息,如:
-
同时提供一个方法
emitMicroAppPropsChange,用于主应用在某些场景下主动推送最新的用户信息/权限到某个微应用:- 根据
microAppList查出微应用的 name - 从主应用的用户 store 中取出权限、菜单、用户信息等
- 通过事件总线向对应微应用发送
xxx:props-change事件
- 根据
这样就形成了两类通信:
- 子应用 → 主应用:通过
bus.$emit('sub-route-change', route)等方式通知主应用 - 主应用 → 子应用:通过
bus.$emit('view-crm-pc:props-change', props)等方式同步最新数据
3.5 路由预加载与体验优化
在路由守卫中(类似 src/router/guard/index.ts 的实现):
- 定义了一个
microAppMap,记录微应用 key 与 name 的映射 - 在
router.beforeEach中:- 判断
to.meta.microApp是否存在 - 若对应微应用 尚未预加载,则调用
preloadApps对它进行预加载 - 这样用户第一次点击某个微应用菜单时,加载会更快
- 判断
这一部分是无界结合项目实际业务做的 性能优化:只在用户 首次进入某个微应用相关路由 时预加载,避免启动时一次性加载所有子应用。
四、乾坤(qiankun)概览
4.1 核心特点
qiankun 是阿里开源的一套微前端框架,整体定位是:
一个基于
single-spa封装的微前端框架,专注于在生产环境里高效落地微前端架构。
主要特点:
- 技术栈无关:子应用可以是 React/Vue/Angular/原生 JS 等
- 按需加载 & 独立部署:每个子应用有自己的仓库、构建、部署进程
- 完善的生命周期管理:
bootstrap/mount/unmount
- JS 沙箱与样式隔离:
- 基于
Proxy等方案构造 JS 沙箱 - 提供多种样式隔离模式(如
strictStyleIsolation等)
- 基于
- 全局状态管理:
- 通过
initGlobalState创建全局状态 - 主子应用都可以订阅/修改,实现跨应用数据同步
- 通过
- 工程化支持:
- 已有大量社区实践和脚手架模板,对接方便
4.2 基于 single-spa 的架构
qiankun 的底层思想来自 single-spa:
- 把每个子应用抽象成:
- 一段 entry(入口)配置:包含 HTML / JS / CSS 等资源
- 一组生命周期函数:
bootstrap、mount、unmount
- 主应用通过 路由规则(
activeRule) 决定何时激活某个子应用 - 在激活/卸载时,调用对应子的生命周期,实现真正的「按需加载」
五、使用 qiankun 的示例(类比当前项目)
下面用一个「主应用 + CRM + 会员」的 qiankun 示例,类比你现在无界的使用方式,帮助你理解这两套框架的差异与共性。
5.1 主应用集成 qiankun
5.1.1 安装依赖
# 主应用安装 qiankun
npm install qiankun --save
# 或
yarn add qiankun
5.1.2 主入口集成(类似当前项目 main.ts)
// main.ts(主应用)
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import { registerMicroApps, start, initGlobalState } from 'qiankun'
const app = createApp(App)
app.use(router)
app.mount('#app')
/**
* 注册微应用,类似你现在的 microAppList
*/
registerMicroApps(
[
{
name: 'crm-app',
entry: 'https://crm.xxx.com/', // CRM 子应用部署地址
container: '#subapp-container', // 挂载点
activeRule: '/crm', // 当主路由以 /crm 开头时激活
props: {
getToken: () => localStorage.getItem('token'),
},
},
{
name: 'member-app',
entry: 'https://member.xxx.com/',
container: '#subapp-container',
activeRule: '/member',
props: {
getToken: () => localStorage.getItem('token'),
},
},
],
{
beforeLoad: app => {
console.log('before load', app.name)
},
afterMount: app => {
console.log('after mount', app.name)
},
afterUnmount: app => {
console.log('after unmount', app.name)
},
},
)
/**
* 全局状态(主子应用共享)
*/
const actions = initGlobalState({
user: null,
token: null,
})
actions.onGlobalStateChange((state, prev) => {
console.log('[onGlobalStateChange - master]:', state, prev)
})
/**
* 启动 qiankun
*/
start()
5.1.3 主应用布局容器
<!-- App.vue 示例 -->
<template>
<div class="layout">
<!-- 头部、侧边菜单等 -->
<div id="subapp-container" class="subapp-container"></div>
</div>
</template>
这一点和你项目中通过 MicroAppContainer 作为承载容器的思路是一致的,只是 qiankun 直接用一个 DOM 容器,而无界更多是通过 WujieVue 组件。
5.2 子应用改造与生命周期
以 Vue3 子应用为例(如 CRM 应用):
// 子应用 main.ts(crm-app)
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
let app: any = null
function render(props: any = {}) {
const { container, getToken } = props
console.log('接收主应用 props', props, 'token:', getToken?.())
app = createApp(App)
// 在这里可以把 token 写入到子应用自己的 store
// const store = useUserStore()
// store.setToken(getToken?.())
app.use(router)
app.mount(container ? container.querySelector('#app') : '#app')
}
// qiankun 生命周期
export async function bootstrap() {
console.log('子应用 bootstrap')
}
export async function mount(props: any) {
console.log('子应用 mount', props)
render(props)
}
export async function unmount() {
console.log('子应用 unmount')
app.unmount()
app = null
}
// 独立运行子应用时(本地开发直接访问)
if (!(window as any).__POWERED_BY_QIANKUN__) {
render()
}
这部分对应到无界里,就是「子应用如何被主应用挂载和初始化」的逻辑。
5.3 主子应用通信示例
qiankun 提供了 initGlobalState 来做全局状态管理,主子应用均可参与。
5.3.1 主应用:初始化与更新全局状态
// 主应用
import { initGlobalState } from 'qiankun'
const actions = initGlobalState({
user: null,
token: null,
})
actions.onGlobalStateChange((state, prev) => {
console.log('[onGlobalStateChange - master]:', state, prev)
})
// 登录成功时更新状态
function onLoginSuccess(user, token) {
actions.setGlobalState({
user,
token,
})
}
5.3.2 子应用:接入全局状态
// 子应用(例如 crm-app 中)
import type { MicroAppStateActions } from 'qiankun'
let actions: MicroAppStateActions
export function initActions(_actions: MicroAppStateActions) {
actions = _actions
actions.onGlobalStateChange((state, prev) => {
console.log('[onGlobalStateChange - sub]:', state, prev)
// 同步到自己应用的 store,如用户信息、权限等
}, true)
}
// 在子应用 mount 中调用
export async function mount(props: any) {
initActions(props.actions) // 主应用通过 props 传入 actions
render(props)
}
和你当前项目无界中的事件总线 bus 类比:
- 无界:偏事件驱动(
bus.$on/bus.$emit) - qiankun:偏状态驱动(
onGlobalStateChange/setGlobalState)
两者本质上都能完成「主子应用通信」的需求,只是表现形式不同。
六、无界 vs 乾坤:对比与选型
6.1 能力对比
| 维度 | 无界(Wujie) | 乾坤(qiankun) |
|---|---|---|
| 技术栈依赖 | 主打前端框架无关,提供 Vue/React 等集成包 | 基于 single-spa,同样技术栈无关 |
| 样式 & JS 隔离 | iframe + Web Components,隔离强 |
沙箱 + 样式隔离(可配置),更偏 JS 劫持 |
| 应用保活 | 内置 alive 特性,支持应用保活 |
通过 keep-alive/缓存等方案实现,需业务方一定参与 |
| 通信方式 | 事件总线(bus)、props 传参 |
initGlobalState 全局状态 + props 传参 |
| 接入复杂度 | 子应用改造较少,接入相对轻量 | 需实现 qiankun 生命周期,入口改造稍多 |
| 社区生态 | 新兴方案,文档和示例也在持续完善 | 生态成熟,案例较多 |
| 与当前项目的契合度 | 已接入:CRM、会员子应用,通信、鉴权、预加载等都跑起来了 | 如需迁移,需要重新梳理路由/生命周期/构建配置等 |
6.2 适用场景建议
-
更看重隔离性 & 接入成本:
- 子应用改造成本要尽可能小
- 希望尽量“开箱即用”
→ 无界(Wujie)相对更友好
-
已有大量 qiankun 经验或基于 single-spa 体系:
- 团队已有 qiankun 使用经验
- 或现有架构已经围绕 qiankun 建立
→ 继续使用 qiankun 更合适
-
你的当前项目情况:
- 已完成无界的接入与实践(包含权限同步、路由预加载、事件通信等)
- 除非有很强的理由(例如要统一到部门既有的 qiankun 标准),否则 继续在无界基础上深挖和优化,是性价比最高的选择。
七、学习与实践建议
结合你本地项目,建议你按下面顺序学习和动手:
-
从主应用入口看起
- 理解
main.ts中如何app.use(WujieVue)、setupWujie() - 明白主应用在哪个 DOM 容器中承载子应用
- 理解
-
彻底读一遍
wujie.ts- 弄清楚
microAppList配置了哪些子应用(CRM、会员等) - 看 degrade、plugins 等配置是解决什么问题的
- 弄清楚
-
理解
MicroAppContainer- 路由
meta.microApp是如何决定当前加载哪个微应用 subAppUrl是如何拼出的subAppProps里到底给子应用传了什么(权限、用户信息等)- 生命周期钩子里分别做了什么事情
- 路由
-
看通信文件(如
micro-app.ts)- 全局事件总线 bus 上都监听了哪些事件
- 子应用如何发起:路由跳转、登录过期通知等
-
再对照本文的 qiankun 示例
- 把无界的做法,在脑海里映射成“如果是 qiankun,我会怎么写”
- 这样你不仅懂「怎么用无界」,也明白「其他框架会怎么解决同样问题」
更多推荐
所有评论(0)