微前端框架对比与实践:无界(Wujie)与乾坤(qiankun)


目录


一、微前端基础概念

微前端(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,自动携带 tokenOrg-IdLogin-User-Id 等头信息,子应用请求后端时自动完成鉴权
  • 生命周期钩子:
    • beforeLoad:开始加载,设置 loading = true,向子应用广播初始化 props
    • afterMount:加载完成,loading = false
    • activated / deactivated:配合应用保活,在激活/切换时同步状态

3.4 主子应用通信

项目中通过无界的 全局事件总线 bus 做通信:

  • 在一个专门的模块中(类似 micro-app.ts)集中处理:

    • 订阅全局消息,如:global:message
    • 处理子应用发起的路由跳转,如:sub-route-change
    • 处理子应用触发的登录过期事件,如:micro-app:session-expired
  • 同时提供一个方法 emitMicroAppPropsChange,用于主应用在某些场景下主动推送最新的用户信息/权限到某个微应用:

    • 根据 microAppList 查出微应用的 name
    • 从主应用的用户 store 中取出权限、菜单、用户信息等
    • 通过事件总线向对应微应用发送 xxx:props-change 事件

这样就形成了两类通信:

  1. 子应用 → 主应用:通过 bus.$emit('sub-route-change', route) 等方式通知主应用
  2. 主应用 → 子应用:通过 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 等资源
    • 一组生命周期函数:bootstrapmountunmount
  • 主应用通过 路由规则(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 标准),否则 继续在无界基础上深挖和优化,是性价比最高的选择

七、学习与实践建议

结合你本地项目,建议你按下面顺序学习和动手:

  1. 从主应用入口看起

    • 理解 main.ts 中如何 app.use(WujieVue)setupWujie()
    • 明白主应用在哪个 DOM 容器中承载子应用
  2. 彻底读一遍 wujie.ts

    • 弄清楚 microAppList 配置了哪些子应用(CRM、会员等)
    • 看 degrade、plugins 等配置是解决什么问题的
  3. 理解 MicroAppContainer

    • 路由 meta.microApp 是如何决定当前加载哪个微应用
    • subAppUrl 是如何拼出的
    • subAppProps 里到底给子应用传了什么(权限、用户信息等)
    • 生命周期钩子里分别做了什么事情
  4. 看通信文件(如 micro-app.ts

    • 全局事件总线 bus 上都监听了哪些事件
    • 子应用如何发起:路由跳转、登录过期通知等
  5. 再对照本文的 qiankun 示例

    • 把无界的做法,在脑海里映射成“如果是 qiankun,我会怎么写”
    • 这样你不仅懂「怎么用无界」,也明白「其他框架会怎么解决同样问题」
Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐