在最近的项目中接触到了qiankun这个技术,以此做个简单的笔记。
qiankun官网入口:https://qiankun.umijs.org/zh/guide
1. 概念
qiankun是一个微前端,主要用于大型的前端应用;
qiankun 是一个基于 single-spa 的微前端实现库,旨在帮助大家能更简单、无痛的构建一个生产可用微前端架构系统。
qiankun 孵化自蚂蚁金融科技基于微前端架构的云产品统一接入平台
几个特点:
- 技术栈无关------主应用和子应用不限制接入的技术框架;
- 独立部署------主应用和子应用都可以独立部署,后端也是独立部署(正常的开发模式);
- 增量升级------在已有的微应用基础之上,可以对单独的子应用进行技术栈升级,比如:vue2升级vue3(不过Vue3用来做微前端好像有点问题,后续慢慢研究)。
- 独立运行时------每个子应用运行时互不影响(状态隔离),比如状态值,事件处理等;
qiankun 的核心设计理念
🥄 简单
由于主应用微应用都能做到技术栈无关,qiankun 对于用户而言只是一个类似 jQuery 的库,你需要调用几个 qiankun 的 API 即可完成应用的微前端改造。同时由于 qiankun 的 HTML entry 及沙箱的设计,使得微应用的接入像使用 iframe 一样简单。
🍡 解耦/技术栈无关
微前端的核心目标是将巨石应用拆解成若干可以自治的松耦合微应用,而 qiankun 的诸多设计均是秉持这一原则,如 HTML entry、沙箱、应用间通信等。这样才能确保微应用真正具备 独立开发、独立运行 的能力。
类似于jquery库,当已有的项目过多时,想让这几个应用串联起来使用,使用微前端就比较合适,比如:医院系统,会细分不同的科室,细分不同的病症处理,细分系统配置,医院的员工管理等等,如果每一个都是单独的应用,用户(医生,医院人员)需要登录不同的网站,操作起来就会很繁琐,而且研发人员进行维护的时候,也会比较困难。
2. 上手使用
2.1 安装
在准备好的主应用项目中安装依赖
javascript
$ yarn add qiankun # 或者 npm i qiankun -S
2.2 配置子应用
javascript
import { registerMicroApps, start } from 'qiankun';
registerMicroApps([
{
name: 'react app', // app name registered
entry: '//localhost:7100',
container: '#yourContainer',
activeRule: '/yourActiveRule',
},
{
name: 'vue app',
entry: { scripts: ['//localhost:7100/main.js'] },
container: '#yourContainer2',
activeRule: '/yourActiveRule2',
},
]);
start();
- name: 微应用的唯一标识
- entry: 入口地址
- container: 微应用挂载的DOM容器选择器 (css挂载的容器等)
- activeRule: 激活规则,当URL路径匹配此前缀时激活该微应用;
- devEntry:开发环境下的入口地址(拓展)
- prodEntry:生产环境下的入口地址(拓展)
- props:主应用和子应用之间进行通信(拓展)
3 微应用
3.1 导出相应的生命周期钩子
微应用需要在自己的入口 js (通常就是你配置的 webpack 的 entry js) 导出
bootstrap、mount、unmount三个生命周期钩子,以供主应用在适当的时机调用。
bootstrap:只在微应用初始化,调用一次,全局变量初始化- mount:每次进入微应用都会加载,处理渲染等
- unmount:卸载微应用时,销毁全局变量等操作
3.2 配置微应用的打包工具
配置微应用的打包工具,以便主应用更好的识别子应用;在微应用的vue.config.js文件中配置相关代码。
javascript
const packageName = require('./package.json').name;
module.exports = {
output: {
library: `${packageName}-[name]`,
libraryTarget: 'umd',
chunkLoadingGlobal: `webpackJsonp_${packageName}`,
},
};
- library:从package.json中获取应用名称
- libraryTarget:将打包方式定义为umd方式,默认是iife项目开发,这里需要将微应用打包为库的形式,给主应用使用;
- chunkLoadingGlobal:定义动态加载快全局变量名称
更多的配置项,可以参考webpack官网:https://webpack.js.org/configuration/output/#outputlibrary

3.3 资源加载
新增
public-path.js文件,用于修改运行时的publicPath。什么是运行时的 publicPath ?
javascript
/* eslint-disable no-undef */
if (window.__POWERED_BY_QIANKUN__) {
__webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__
}
3.3.1 问题背景
在微前端架构中,子应用可能面临以下资源加载路径问题:
- 独立部署 vs 微前端部署的区别
-
独立部署时 : 子应用有自己固定的部署路径,如
https://example.com/child-app/ -
微前端部署时 : 子应用被嵌入到主应用路由下,如
https://main-app.com/child-app/
- 资源路径不一致问题
javascript
// 子应用构建时的静态资源配置
{
"publicPath": "/b/", // 构建时指定的路径
"js": "app.js", // 相对路径
"css": "style.css"
}
当子应用在主应用的 /a/b/ 路由下运行时,浏览器仍会尝试从 /b/app.js 加载资源,而不是期望的 /a/b/app.js。
3.3.2 解决方案机制
1. 动态路径修正
javascript
// 主应用注入正确的基础路径
window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__ = 'https://main-app.com/a/b/'
// 子应用接收并设置
__webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__
2. 资源加载过程
javascript
// 设置前:资源尝试从错误路径加载
// https://main-app.com/b/app.js (404)
// 设置后:资源从正确路径加载
// https://main-app.com/a/b/app.js (成功)
3.4 实际应用场景示例
场景1:异步组件加载
javascript
// 在微前端环境下,动态导入组件
const AsyncComponent = () => import('./components/AsyncComponent.vue')
// 如果没有设置 __webpack_public_path__,会尝试从构建时的路径加载
// 设置后,会从主应用注入的路径加载
场景2:静态资源引用
javascript
// CSS 中的背景图引用
.some-class {
background-image: url('./images/bg.png'); // 实际加载路径会被前缀修正
}
// JS 中的资源加载
const imageUrl = new URL('./assets/logo.png', import.meta.url).href
4. 主应用生命周期钩子函数
javascript
registerMicroApps(apps, lifeCycles?)
- apps : 微应用配置数组,每个应用包含名称、入口、容器、激活规则等信息
- lifeCycles : (可选) 全局生命周期配置对象
-
LifeCycles- beforeLoad -
Lifecycle | Array<Lifecycle>- 可选 应用加载前 - beforeMount -
Lifecycle | Array<Lifecycle>- 可选 挂载前 - afterMount -
Lifecycle | Array<Lifecycle>- 可选 挂载后 - beforeUnmount -
Lifecycle | Array<Lifecycle>- 可选 卸载前 - afterUnmount -
Lifecycle | Array<Lifecycle>- 可选 卸载后
- beforeLoad -
-
所有生命周期函数都是异步函数,接受应用配置对象作为参数
-
返回Promise,允许异步操作完成后再继续执行
4.1 执行流程
- beforeLoad → 2. 加载资源 → 3. beforeMount → 4. 挂载到DOM → 5. afterMount
- 路由切换时:beforeUnmount → 7. 从DOM卸载 → 8. afterUnmount
这些生命周期钩子提供了完整的应用状态管理能力,确保微应用在各个阶段都能正确执行相应操作。
5. 运行主应用
start(opts?)
Options
prefetch -
boolean | 'all' | string[] | (( apps: RegistrableApp[] ) => { criticalAppNames: string[]; minorAppsName: string[] })- 可选,是否开启预加载,默认为true。配置为
true则会在第一个微应用 mount 完成后开始预加载其他微应用的静态资源配置为
'all'则主应用start后即开始预加载所有微应用静态资源配置为
string[]则会在第一个微应用 mounted 后开始加载数组内的微应用资源配置为
function则可完全自定义应用的资源加载时机 (首屏应用及次屏应用)
javascript
案例:
start({
prefetch: false, // 预加载
sandbox: {
// js沙箱:默认情况下支持proxy的话就是用proxy沙箱,否则就使用snapshotsandbox
// css沙箱默认不开启,只有sandbox为对象且配置了experimentalStyleIsolation或者strictStyleIsolation时才会开启css沙箱
experimentalStyleIsolation: true, // scope
// strictStyleIsolation: true, // ShadowDom
},
singular: false, // 是否为单实例场景,单实例指的是同一时间只会渲染一个微应用。默认为 true。
excludeAssetFilter: (assetUrl) => {},
})
sandbox:沙箱隔离
strictStyleIsolation这个在最新qiankun版本好像被废弃了
experimentalStyleIsolation: true 为微应用添加样式隔离,样式语法前缀
但是对于微应用和主应用同时添加了根样式,比如:body,html样式时,不生效。
6. 通信
官方推荐的API:initGlobalState(state)
在主应用中定义变量属性,主应用通过registerMicroApps中props传递数据信息,微应用在mount中获取;
主应用
javascript
{
name: 'b',
devEntry: '/b/',
prodEntry: '/b/',
container: '#app',
activeRule: '/a/b/',
props: {
name:1 // 传递给子应用的数据
},
},
微应用
javascript
export async function mount(props) {
console.log('props:', props)
}
当然除了官网提供的方法之外,还有其他方法,比如:
- localStorage/sessionStorage 浏览器器缓存机制
- 通过路由参数共享
- 官方提供的 actions
- 使用vuex或redux状态管理,通过shared分享
- window全局对象通信
具体用哪个方法,可以根据项目需求进行取舍。
基本的乾坤配置就是这些,还有些细节没有具体说明,后续学习到更多关于qiankun知识点再追述一下。