一、前言
在微前端架构中,状态管理是一个重要的课题。由于子应用是独立的,它们之间可能需要共享状态或通信。以下是基于
qiankun
微前端架构的状态管理方案,结合Vue 3
和Vite
的实现。
二、状态管理方案
在微前端中,状态管理可以分为以下几种方式:
1. 主应用和子应用共享状态:
- 通过全局状态管理库(如
Pinia
或Vuex
)在主应用中管理全局状态,子应用通过主应用提供的接口访问或修改状态。 - 使用
qiankun
的initGlobalState
方法实现主应用和子应用之间的状态共享。
2. 子应用独立状态管理:
- 每个子应用使用自己的状态管理库(如
Pinia
或Vuex
),状态独立管理,不与其他子应用共享。
3. 事件通信:
- 使用
CustomEvent
或EventBus
实现子应用之间的通信。
三、使用 qiankun
的 initGlobalState
实现状态共享
qiankun
提供了initGlobalState
方法,用于在主应用和子应用之间共享状态。
1. 主应用配置
在主应用中初始化全局状态。
// src/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');
// 初始化全局状态
const initialState = {
user: {
name: 'Main App User',
},
};
const actions = initGlobalState(initialState);
// 监听状态变化
actions.onGlobalStateChange((state, prevState) => {
console.log('主应用:状态变化', state, prevState);
});
// 注册子应用
registerMicroApps([
{
name: 'subApp1',
entry: '//localhost:7101',
container: '#subapp-container',
activeRule: '/subApp1',
},
{
name: 'subApp2',
entry: '//localhost:7102',
container: '#subapp-container',
activeRule: '/subApp2',
},
]);
// 启动 qiankun
start();
2. 子应用配置
在子应用中获取和修改全局状态。
// subApp1/src/main.ts
import { createApp } from 'vue';
import App from './App.vue';
import router from './router';
let instance = null;
function render(props = {}) {
const { container } = props;
instance = createApp(App);
instance.use(router);
instance.mount(container ? container.querySelector('#app') : '#app');
// 获取全局状态
if (props.onGlobalStateChange && props.setGlobalState) {
props.onGlobalStateChange((state, prevState) => {
console.log('子应用1:状态变化', state, prevState);
});
// 修改全局状态
props.setGlobalState({
user: {
name: 'SubApp1 User',
},
});
}
// 将 props 注入到 Vue 实例中
instance.provide('MAIN_APP_PROPS', props);
}
if (!window.__POWERED_BY_QIANKUN__) {
render();
}
export async function bootstrap() {
console.log('subApp1 bootstrap');
}
export async function mount(props) {
console.log('subApp1 mount');
render(props);
}
export async function unmount() {
console.log('subApp1 unmount');
instance.unmount();
instance = null;
}
3. Vue实例中修改全局变量
在配置中已经将主应用传过来的prop对象参数,使用依赖注入的方式注入到全局
instance.provide('MAIN_APP_PROPS', props);
vue实例中可以使用inject获取这个参数,从而使用prop参数里面带的onGlobalStateChange、setGlobalState两个函数管理全局状态
/**
* test.vue TODO
* @Author ZhangJun
* @Date 2025/3/12 15:03
**/
<template>
<el-card class="text-left">
<template #header>
<div>子应用1测试修改全局状态</div>
</template>
<div>
<div>当前全局状态:</div>
<div class="bg-blue-lighter">
<el-input
v-model="globalStateCache"
style="width: 240px"
:autosize="{ minRows: 2, maxRows: 4 }"
type="textarea"
placeholder="请输入新的全局状态"
/>
</div>
</div>
<el-button style="margin-top: 10px" type="primary" @click="changeGlobalState()">修改全局状态</el-button>
</el-card>
</template>
<script setup lang="ts">
import {inject, ref} from "vue";
const mainAppProps:any = inject('MAIN_APP_PROPS')
//全局状态缓存
const globalStateCache = ref('')
//监听全局状态变化
mainAppProps?.onGlobalStateChange((state:any,prev:any) => {
if(JSON.stringify(state)!== JSON.stringify(prev)){
globalStateCache.value = JSON.stringify(state)
}
})
/**
* 全局状态的修改
*/
const changeGlobalState = () => {
let updateState = JSON.parse(globalStateCache.value)
//更新全局状态
mainAppProps?.setGlobalState(updateState)
}
</script>
<style scoped>
.bg-blue-lighter{
background-color: #f0f9ff;
color: #4a4a4a;
}
</style>
四、使用 Pinia
实现子应用独立状态管理
如果子应用需要独立管理状态,可以使用Pinia
(推荐)或Vuex
。
1. 安装 Pinia
在子应用中安装Pinia
:
npm install pinia
2. 配置 Pinia
在子应用中创建Pinia
实例并注入到应用中。
// subApp1/src/main.ts
import { createApp } from 'vue';
import { createPinia } from 'pinia';
import App from './App.vue';
import router from './router';
let instance = null;
function render(props = {}) {
const { container } = props;
const app = createApp(App);
const pinia = createPinia();
app.use(pinia);
app.use(router);
instance = app.mount(container ? container.querySelector('#app') : '#app');
}
if (!window.__POWERED_BY_QIANKUN__) {
render();
}
export async function bootstrap() {
console.log('subApp1 bootstrap');
}
export async function mount(props) {
console.log('subApp1 mount');
render(props);
}
export async function unmount() {
console.log('subApp1 unmount');
instance.unmount();
instance = null;
}
3. 创建 Pinia Store
在子应用中创建Pinia Store
。
// subApp1/src/stores/userStore.js
import { defineStore } from 'pinia';
export const useUserStore = defineStore('user', {
state: () => ({
name: 'SubApp1 User',
}),
actions: {
updateName(newName) {
this.name = newName;
},
},
});
4. 在组件中使用 Pinia Store
<!-- subApp1/src/App.vue -->
<template>
<div>
<h1>{{ userStore.name }}</h1>
<button @click="updateName">Update Name</button>
</div>
</template>
<script>
import { useUserStore } from './stores/userStore';
export default {
setup() {
const userStore = useUserStore();
const updateName = () => {
userStore.updateName('New SubApp1 User');
};
return {
userStore,
updateName,
};
},
};
</script>
五、使用 EventBus
实现子应用通信
如果子应用之间需要通信,可以使用EventBus
。
在主应用中创建一个全局事件总线,并将其注入到子应用中。
1. 创建 EventBus
在主应用中创建一个eventBus.js
文件,用于导出事件总线实例。
// src/eventBus.js
import mitt from 'mitt'
const eventBus = mitt()
export default eventBus;
2. 主应用注入事件总线
在主应用中,通过registerMicroApps
的props
参数将事件总线传递给子应用。
// src/main.js
import { createApp } from 'vue';
import App from './App.vue';
import router from './router';
import { registerMicroApps, start } from 'qiankun';
import eventBus from './eventBus';
const app = createApp(App);
app.use(router);
app.mount('#app');
// 注册子应用
registerMicroApps([
{
name: 'subApp1',
entry: '//localhost:7101',
container: '#subapp-container',
activeRule: '/subApp1',
props: {
eventBus, // 将事件总线传递给子应用
},
},
{
name: 'subApp2',
entry: '//localhost:7102',
container: '#subapp-container',
activeRule: '/subApp2',
props: {
eventBus, // 将事件总线传递给子应用
},
},
]);
// 启动 qiankun
start();
3. 在子应用中使用 EventBus
在子应用中,通过props
获取事件总线,并使用它来发送和接收事件。
3.1 子应用1 发送事件
在子应用1中,通过事件总线发送事件。
<!-- subApp1/src/views/Home.vue -->
<template>
<div>
<h1>子应用1</h1>
<button @click="sendMessage">发送消息</button>
</div>
</template>
<script>
import { inject } from 'vue';
export default {
setup() {
const props = inject('props');
const eventBus = props.eventBus;
const sendMessage = () => {
eventBus.emit('message-from-subApp1', {
message: 'Hello from SubApp1',
});
};
return {
sendMessage,
};
},
};
</script>
3.2 子应用2 监听事件
在子应用2中,通过事件总线监听事件。
<!-- subApp2/src/views/Home.vue -->
<template>
<div>
<h1>子应用2</h1>
<p>收到消息:{{ message }}</p>
</div>
</template>
<script>
import { inject, ref, onMounted, onUnmounted } from 'vue';
export default {
setup() {
const props = inject('props');
const eventBus = props.eventBus;
const message = ref('');
const handleMessage = (payload) => {
message.value = payload.message;
};
onMounted(() => {
eventBus.on('message-from-subApp1', handleMessage);
});
onUnmounted(() => {
eventBus.off('message-from-subApp1', handleMessage);
});
return {
message,
};
},
};
</script>
六、总结
- 全局状态共享:使用
qiankun
的initGlobalState
方法。 - 子应用独立状态管理:使用
Pinia
或Vuex
。 - 子应用通信:使用
EventBus
或CustomEvent
。