前言
继续上文的项目进行关于qiankun集成主子应用的通信,目前我翻阅了不少相关的pinia通信的资料,大部分其实就是分为两种:action通信以及共享store,但是我实际项目应用中我的pinia实例是响应式的,我需要主子应用响应式的进行通信,所以目前遇到的文章资料都没满足我的需求(也可能是我配置的还是不太正确,我没搞通),所以我会介绍一下我是如何进行pinia通信的,主要应用的也是initGlobalState通信,另外附带项目源码地址在文章最后。如果项目还没搭建好的话,建议看一下我的上一篇:qiankun集成主子应用创建篇 。
项目环境
项目环境和上一篇是一样的,只要添加了pinia以及持久化存储pinia实例到local storage的插件就行,主子应用都要添加的,子应用添加是方便子应用独立运行的时候可以正常使用pinia实例。
npm install pinia -s
npm install pinia-plugin-persistedstate -s
主应用部分
首先是main.js中,我们要把pinia注册好,这里我直接引用了我的pinia的初始化文件,因为要集成持久化,所以单独出来了js文件,该文件同时引入了一个message实例:
同时main.js引入该文件:
import pinia from ‘@/store/index’
app.use(pinia)
然后在主应用的home.vue页面展示一下主应用的pinia实例:
效果就是可以看到,在主应用的home页面出现了蓝色字体:
同时我在主应用的App.vue页面写了一个按钮,目的是在主应用中修改主应用的message实例内容,这个后面会提到。
接下来配置好主应用的action通信,在App.vue入口文件监听共享的实例信息响应:
//设置全局通信
import actions from './actions'
//因为路由不能持久化存储,所以子应用单独获取动态路由数据
const piniaExample = ref({
messageStore: messageStore,
})
/* 设置全局状态 */
actions.setGlobalState(piniaExample.value)
/* 获取全局状态 */
actions.onGlobalStateChange((state, prev) => {
//state:变更后的状态;prev:变更前的状态
piniaExample.value = state
})
watch(piniaExample.value, (newValue, oldValue) => {
piniaExample.value = newValue
actions.setGlobalState(piniaExample.value)
})
设置按钮和方法,在主应用改变实例信息,测试子应用可以收到改变后的信息:
<el-button style="margin-top: 10px" @click="changeToMain()">
改变消息为:主应用实例
</el-button>
import { useMessageStore } from '@/store'
const messageStore = useMessageStore()
function changeToMain() {
messageStore.setMessage('主应用实例')
}
子应用部分
在子应用中配置Action通信,根目录创建shared文件夹,并创建好action.js,globalStateManager.js,useGlobalState.js三个Js文件,其中在main.js中判断,若是作为qiankun子应用运行,则需要注册actions,否则独立运行使用自身pinia实例:
if (qiankunWindow.__POWERED_BY_QIANKUN__) {
actions.setActions(props)
//获取全局状态
await addGlobalStateListener((state, prev) => {
messageStore = state.messageStore
})
} else {
messageStore = useMessageStore()
}
action.js
function emptyAction() {
// 警告:提示当前使用的是空 Action
console.warn('Current execute action is empty!')
return null
}
class Actions {
// 默认值为空 Action
actions = {
onGlobalStateChange: emptyAction,
setGlobalState: emptyAction,
}
/* 设置 actions */
setActions(actions) {
this.actions = actions
}
/** 映射*/
onGlobalStateChange(...args) {
return this.actions.onGlobalStateChange(...args)
}
/*** 映射*/
setGlobalState(...args) {
return this.actions.setGlobalState(...args)
}
}
const actions = new Actions()
export default actions
globalStateManager.js
// globalStateManager.js
let unsubscribe
let listeners = []
let state = {}
// 添加状态变化监听器
export function addGlobalStateListener(callback) {
if (typeof callback === 'function') {
listeners.push(callback)
}
}
// 获取当前状态
export function getGlobalState() {
return state
}
// 初始化全局状态监听器
export function initGlobalStateListener(actions) {
if (!unsubscribe) {
unsubscribe = actions.onGlobalStateChange((newState, prev) => {
state = { ...state, ...newState } // 更新全局状态
listeners.forEach((listener) => listener(newState, prev))
}, true)
}
}
// 移除特定的状态变化监听器
export function removeGlobalStateListener(callback) {
listeners = listeners.filter((listener) => listener !== callback)
}
// 移除全局状态监听器
export function clearGlobalStateListener() {
if (unsubscribe) {
unsubscribe()
unsubscribe = null
listeners = []
state = {}
}
}
useGlobalState.js
// useGlobalState.js
import { ref, onMounted, onUnmounted } from 'vue'
import { getGlobalState, addGlobalStateListener, removeGlobalStateListener } from './globalStateManager'
export function useGlobalState() {
const globalStore = ref(getGlobalState())
const updateState = (state) => {
globalStore.value = state
}
// 添加全局状态变化监听器
const listener = (state) => {
updateState(state)
}
onMounted(() => {
addGlobalStateListener(listener)
})
onUnmounted(() => {
removeGlobalStateListener(listener)
})
return {
globalStore
}
}
配置好这些,在home.vue就可以使用了,同时设置按钮测试是否成功:
<template>
<div style="display: flex; flex-direction: column">
<div>
<a href="https://vite.dev" target="_blank">
<img src="/vite.svg" class="logo" alt="Vite logo" />
</a>
</div>
<div>
<h1>这是子应用Home页面</h1>
</div>
<div style="color: green">
<h1>{{ messageStore.message }}</h1>
<button @click="changeMessage">
改变主应用消息为:这是改变的子应用实例
</button>
</div>
</div>
</template>
<script setup>
import { ref, watch, watchEffect, unref, toRefs } from 'vue'
import { useMessageStore } from '../store/index'
var messageStore = ref({})
import { useGlobalState } from '@/shared/useGlobalState'
const { globalStore } = useGlobalState()
import { qiankunWindow } from 'vite-plugin-qiankun/dist/helper.js'
if (!qiankunWindow.__POWERED_BY_QIANKUN__) {
messageStore = useMessageStore()
} else {
watchEffect(() => {
if (globalStore.value) {
messageStore.value = globalStore.value.messageStore
}
})
}
function changeMessage() {
messageStore.value.setMessage('这是改变的子应用实例')
}
</script>
<style scoped>
.logo {
height: 6em;
padding: 1.5em;
will-change: filter;
transition: filter 300ms;
}
.logo:hover {
filter: drop-shadow(0 0 2em #646cffaa);
}
.logo.vue:hover {
filter: drop-shadow(0 0 2em #42b883aa);
}
</style>
现在子应用展示的pinia实例是主应用的pinia实例,并且在需改的时候,主应用的pinia实例可以同步修改,在子应用展示过程中,主应用修改pinia实例,子应用也可以同步变更,实现了我们的需求:
完整通信示例
附上完整git地址:
Main主应用:Main主应用 https://github.com/Tzien/QiankunViteMain
Sub子应用:Sub子应用 https://github.com/Tzien/QiankunViteSub
请持续关注,如果觉着对你有用,不妨动动发财小手点个赞👍,创作不易,如有引用,请务必标明出处。
欢迎各位评论区留言互相交流,谢谢!