微服务 Qiankun使用
你可以使用任何你熟悉的事件库(如mitt)来实现一个简单的发布-订阅模式。在主应用中创建 Event Bus 并导出。通过props将$on$emit等方法传递给微应用。主应用和微应用分别进行事件的监听和触发。这种方式更灵活,但需要自己维护事件机制。// 主应用派发事件}));// 子应用监听事件console.log('收到事件:', e.detail);});
qiankun 微前端框架的使用。内容将分为以下几个核心部分:
- qiankun 是什么?
- 主应用(Master Application)的配置与常用 API
- 微应用(Micro Application / Sub-Application)的配置
- 主应用与微应用间的通信方法
- CSS 样式隔离的方式
- 常见问题与注意事项
1. qiankun 是什么?
qiankun 是一个基于 single-spa 的微前端实现库。它旨在帮助大家能更简单、无痛地构建一个生产可用的微前端架构系统。
- 核心思想:将一个大型的单页应用(SPA)拆分为多个更小、更简单的“微应用”。这些微应用可以独立开发、独立部署、独立运行,最后由主应用统一协调和集成。
- 技术栈无关:主应用和微应用可以使用不同的前端框架(React, Vue, Angular, jQuery等)。
2. 主应用(Master Application)的配置与常用 API
主应用负责注册、加载、卸载微应用,并提供公共的依赖和通信机制。
安装
npm install qiankun -S
# 或
yarn add qiankun
常用 API 与配置
-
registerMicroApps(apps, lifeCycles?)- 核心API,注册微应用
这是最常用的方式,用于注册一组微应用。当浏览器的 URL 发生变化时,它会自动匹配并加载对应的微应用。// main.js (主应用入口文件) import { registerMicroApps, start } from 'qiankun'; registerMicroApps([ { name: 'vue-app', // 微应用名称,唯一标识 entry: '//localhost:7101', // 微应用的入口地址(开发时是dev-server地址,生产时是部署后的URL) container: '#subapp-viewport', // 微应用挂载到主应用的哪个DOM节点上 activeRule: '/vue', // 激活规则,当URL以/vue开头时,加载这个微应用 // 可选,传递给微应用的 props,用于通信 props: { token: 'main-app-token', onGlobalStateChange: mainAppGlobalStateChange, setGlobalState: mainAppSetGlobalState, } }, { name: 'react-app', entry: '//localhost:7102', container: '#subapp-viewport', activeRule: '/react', props: { ... } }, ], { // 可选的全局生命周期钩子 beforeLoad: (app) => { console.log('before load', app.name); }, beforeMount: (app) => { console.log('before mount', app.name); }, afterMount: (app) => { console.log('after mount', app.name); }, beforeUnmount: (app) => { console.log('before unmount', app.name); }, afterUnmount: (app) => { console.log('after unmount', app.name); }, }); // 启动 qiankun start(); -
start(options?)- 启动 qiankun
在注册完微应用后,调用start()方法来启动 qiankun。start({ sandbox: { strictStyleIsolation: true }, // 开启严格的样式隔离 prefetch: 'all', // 预加载策略:'all' | '[]' | function singular: true, // 是否为单实例场景(同一时间只加载一个微应用) }); -
setDefaultMountApp(appLink)- 设置默认进入的微应用
用于指定主应用启动后默认进入哪个微应用。import { setDefaultMountApp } from 'qiankun'; // 当主应用启动后,默认会跳转到 /vue 路由,从而加载 vue-app setDefaultMountApp('/vue'); -
runAfterFirstMounted(effect)- 第一个微应用挂载后的回调
常用于一些在微应用加载后才需要执行的逻辑,比如监控。runAfterFirstMounted(() => { console.log('第一个微应用已挂载'); }); -
loadMicroApp(app, configuration?)- 手动加载微应用
适用于需要更精细控制微应用加载/卸载的场景(例如一个页面同时运行多个微应用),它返回一个microApp对象用于后续的卸载。import { loadMicroApp } from 'qiankun'; // 在某个组件内 this.microApp = loadMicroApp({ name: 'vue-app', entry: '//localhost:7101', container: '#vue-container', props: { ... } }); // 在组件卸载时,手动卸载微应用 // this.microApp.unmount();
3. 微应用(Micro Application)的配置
微应用不需要安装 qiankun,但需要暴露三个生命周期钩子函数(bootstrap, mount, unmount)供主应用在适当的时机调用。
对于使用 webpack 构建的微应用(以 Vue/React 为例)
-
导出生命周期钩子:
// main.js (微应用的入口文件) import Vue from 'vue'; import App from './App.vue'; import router from './router'; let instance = null; function render(props = {}) { const { container } = props; instance = new Vue({ router, render: h => h(App), }).$mount(container ? container.querySelector('#app') : '#app'); // 重要:使用主应用传来的 container } // 独立运行时,直接渲染 if (!window.__POWERED_BY_QIANKUN__) { render(); } // 导出 qiankun 需要的三个生命周期函数 export async function bootstrap() { console.log('[vue] vue app bootstraped'); } export async function mount(props) { console.log('[vue] props from main framework', props); // 存储从主应用传来的方法,用于通信 Vue.prototype.$onGlobalStateChange = props.onGlobalStateChange; Vue.prototype.$setGlobalState = props.setGlobalState; render(props); } export async function unmount() { instance.$destroy(); instance.$el.innerHTML = ''; instance = null; } -
配置 webpack (vue.config.js):
必须跨域,并且输出格式为umd。const { defineConfig } = require('@vue/cli-service') const packageName = require('./package.json').name; module.exports = defineConfig({ transpileDependencies: true, devServer: { port: 7101, // 确保端口与主应用注册的entry一致 headers: { 'Access-Control-Allow-Origin': '*', // 允许主应用跨域访问 }, }, configureWebpack: { output: { library: `${packageName}-[name]`, libraryTarget: 'umd', // 将微应用的 library 打包为 umd 格式 jsonpFunction: `webpackJsonp_${packageName}`, // webpack 5 以下需要,解决 webpackJsonp 冲突问题 // chunkLoadingGlobal: `webpackJsonp_${packageName}`, // webpack 5 使用这个 }, }, });
4. 主应用与微应用间的通信方法
qiankun 官方提供了两种通信方式:
方式一:基于 props 的通信(官方 Actions 通信)
这是最推荐的方式。主应用通过 registerMicroApps 或 loadMicroApp 的 props 参数向下传递数据和方法。
-
主应用初始化状态并传递方法:
// main-app/src/shared/actions.js import { initGlobalState } from 'qiankun'; const initialState = { user: { name: 'Main App' }, token: 'abc123', }; const actions = initGlobalState(initialState); // 监听状态变化 actions.onGlobalStateChange((state, prevState) => { // state: 变更后的状态;prevState: 变更前的状态 console.log('主应用监听到状态变化: ', state, prevState); }); // 封装一个设置状态的方法,传递给微应用 export const setGlobalState = (state) => { // 按一级属性设置状态,微应用只能修改已存在的一级属性 actions.setGlobalState(state); }; export default actions;// 在主应用注册微应用时传入 import actions from './shared/actions'; registerMicroApps([ { name: 'vue-app', entry: '//localhost:7101', container: '#subapp-viewport', activeRule: '/vue', props: { // 传递数据和通信方法 onGlobalStateChange: actions.onGlobalStateChange, setGlobalState: actions.setGlobalState, ...initialState // 传递初始状态 } }, ]); -
微应用使用状态和方法:
// 在微应用的 mount 生命周期中接收 export async function mount(props) { console.log('从主应用传来的数据: ', props.user, props.token); // 监听全局状态变化 props.onGlobalStateChange((state, prev) => { console.log('微应用监听到状态变化: ', state, prev); // 更新微应用自身的状态,例如 Vue 的 data 或 React 的 state }, true); // true 表示立即执行一次 // 更新全局状态 props.setGlobalState({ user: { ...props.user, name: 'Vue App Modified' }, }); }
方式二:自定义事件(Event Bus)
你可以使用任何你熟悉的事件库(如 mitt, EventEmitter)来实现一个简单的发布-订阅模式。
- 在主应用中创建 Event Bus 并导出。
- 通过
props将$on,$emit等方法传递给微应用。 - 主应用和微应用分别进行事件的监听和触发。
这种方式更灵活,但需要自己维护事件机制。
// 主应用派发事件
window.dispatchEvent(new CustomEvent('main-event', {
detail: { type: 'NOTIFY', payload: 'Hello' }
}));
// 子应用监听事件
window.addEventListener('main-event', (e) => {
console.log('收到事件:', e.detail);
});
5. CSS 样式隔离的方式
qiankun 提供了两种可选的样式隔离方案,在 start 函数中配置。
-
strictStyleIsolation(严格样式隔离)- 原理:通过 Shadow DOM 将每个微应用包裹起来,实现真正的 DOM 隔离。
- 配置:
start({ sandbox: { strictStyleIsolation: true } }) - 优点:隔离性最强。
- 缺点:微应用内的弹窗(Modal, Drawer)等需要挂载到 body 下的组件,会因为不在 Shadow DOM 内而丢失样式。需要额外处理。
-
experimentalStyleIsolation(实验性样式隔离)- 原理:qiankun 会动态地为微应用的所有样式选择器添加一个特殊的前缀(例如
div[name='vue-app']),将其限制在微应用的容器内。 - 配置:
start({ sandbox: { experimentalStyleIsolation: true } }) - 优点:解决了大部分样式冲突问题,且对弹窗等组件更友好。
- 缺点:不是 100% 隔离,对于动态插入的样式(如
style标签、第三方UI库的样式)可能处理不完美。
- 原理:qiankun 会动态地为微应用的所有样式选择器添加一个特殊的前缀(例如
最佳实践:
- 大部分场景下,使用
experimentalStyleIsolation即可。 - 对于UI库的样式,尽量使用按需引入,避免引入全量全局样式。
- 微应用自身的样式,使用 CSS Modules 或 Scoped CSS(Vue)来避免样式污染。
- 如果主应用和微应用使用了不同的 UI 库(如主应用用 Antd,微应用用 Element),需要注意其全局样式类名(如
.btn)可能冲突,需要为其中一方或双方添加命名空间。
6. 常见问题与注意事项
-
路由问题:
- 主应用和微应用的路由模式(hash / history)可以混用,但通常建议统一。
- 微应用需要设置
base路径(例如 Vue Router 的base选项),使其路由能在主应用的活动规则下正常工作。
-
公共依赖(如 Vue, React):
- 可以通过
webpack的externals将公共库从打包文件中排除,由主应用统一提供(html-entry方式),减少体积。 - 也可以不排除,qiankun 的沙箱可以避免全局污染,但会重复加载。
- 可以通过
-
动态样式丢失: 在使用
strictStyleIsolation时,挂载到document.body的组件会丢失样式,需要将样式插入到 Shadow DOM 外部的document.head中。 -
开发环境跨域: 微应用的 dev-server 必须设置
headers: { 'Access-Control-Allow-Origin': '*' }。 -
生命周期函数: 微应用导出的生命周期函数必须是
Promise或者async函数。
更多推荐



所有评论(0)