qiankun集成主子应用—如何通过pinia进行通信(含git地址)

qiankun集成主子应用—如何通过pinia进行通信

前言

继续上文的项目进行关于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
请持续关注,如果觉着对你有用,不妨动动发财小手点个赞👍,创作不易,如有引用,请务必标明出处。
欢迎各位评论区留言互相交流,谢谢!在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值