qiankun 核心概念解析:路由联动、样式隔离、跨应用通信

4 阅读6分钟

在上一篇文章《微前端入门: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 子路由

关键点

  1. activeRule 匹配规则activeRule: '/app1' 表示 URL 路径 /app1 开头就激活这个微应用。所以微应用内部所有路由都以 /app1 开头。

  2. 基路径动态设置:微应用的路由基路径需要根据运行环境动态设置:

history: createWebHistory(qiankunWindow.__POWERED_BY_QIANKUN__ ? '/app1' : '/')
  • 在 qiankun 环境下:基路径是 /app1,和 activeRule 一致
  • 独立运行时:基路径是 /,方便开发调试
  1. 为什么都要用 history 模式?

    • hash 模式也可以,但 history 模式地址栏更好看
    • 双方都用 history 模式,地址栏可以正确同步
    • 刷新页面后,路由可以正确匹配到微应用内部
  2. 刷新页面 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。但大多数场景不需要。

通信最佳实践

  1. 单向数据流:尽量保持数据从主应用流向微应用,微应用修改后通知主应用
  2. 及时取消监听unmount 生命周期一定要取消监听,否则会内存泄漏
  3. 不要过度通信:如果一个数据只在一个微应用里用,就不要放到全局状态

四、生命周期完整解读

qiankun 微应用有三个核心生命周期:bootstrapmountunmount

生命周期调用时机应该做什么
bootstrap微应用第一次加载前调用只调用一次,做一些全局初始化,比如加载字典数据
mount微应用激活需要挂载时调用,每次激活都会调用渲染应用,创建路由,绑定事件监听
unmount微应用切换走后调用销毁实例,清理定时器,移除事件监听,取消全局状态监听 → 防止内存泄漏
stateDiagram-v2
    [*] --> bootstrap: 首次加载
    bootstrap --> mount: 激活
    mount --> unmount: 切换走
    unmount --> mount: 再次激活

常见问题

Q: 为什么要每次 mount 都重新创建路由?

A: 如果路由只创建一次,存在变量里,上次的状态会残留到下次挂载。每次重新创建可以保证每次都是干净的状态,避免奇怪的 bug。虽然多一点创建开销,但完全可以忽略。

Q: unmount 必须做哪些清理工作?

A:

  1. 销毁 Vue 实例:app?.unmount()
  2. 置空实例引用:app = null
  3. 取消全局状态监听:调用 unsubscribe
  4. 清理定时器:clearInterval(timerId)
  5. 移除全局事件监听:window.removeEventListener

FAQ

Q: 样式隔离不开启可以吗?

A: 如果你的项目只有一个团队维护,大家都遵守命名规范,可以不开启。但还是推荐开启 experimentalStyleIsolation + 根类名,双重保障更安全。

Q: 多个微应用同时挂载在页面上可以吗?

A: 可以。qiankun 支持多个微应用同时存在,你只需要给每个微应用配置不同的 container 容器和不同的 activeRule 就行。

Q: 路由激活后,微应用没有重新渲染怎么办?

A: 检查 activeRule 是否匹配正确,URL 前缀是否和 activeRule 一致。还要检查 unmount 是否正确执行了。

Q: 主应用可以直接调用微应用的方法吗?

A: 不推荐。通过 onGlobalStateChange / setGlobalState 通信更解耦。主应用不应该知道微应用内部的具体方法。

本章小结

掌握了这三个核心概念,你就能解决 qiankun 开发中 80% 的问题:

  1. 路由联动: 主应用匹配前缀激活,微应用处理自己的子路由,基路径动态设置
  2. 样式隔离: experimentalStyleIsolation + 根类名命名空间,兼容性最好
  3. 跨应用通信: 内置 initGlobalState 足够用,记得 unmount 取消监听
  4. 生命周期: bootstrap 一次,每次 mount / unmount 要做正确的清理

下一篇我们进入生产环境环节:打包优化、部署方案、常见坑点汇总,解决你上线会遇到的各种问题。