qiankun框架中基于actions机制实现主应用与子应用间的双向通信

简介: qiankun框架中基于actions机制实现主应用与子应用间的双向通信

一、原理

qiankun 内部提供了 initGlobalState 方法用于注册 MicroAppStateActions 实例用于通信,该实例有三个方法,分别是onGlobalStateChange、setGlobalState、offGlobalStateChange。

1、setGlobalState:

设置 globalState - 设置新的值时,内部将执行 浅检查,如果检查到 globalState 发生改变则触发通知,通知到所有的 观察者 函数。

  • 语法:
  • setGlobalState: (state) => boolean, //按一级属性设置全局状态,微应用中只能修改已存在的一级属性(就是用来修改全局状态的


2、onGlobalStateChange:

注册 观察者 函数 - 响应 globalState 变化,在 globalState 发生改变时触发该 观察者 函数。


语法:

onGlobalStateChange: (callback) => void //在当前应用监听全局状态,有变更触发 callback,fireImmediately = true 立即触发 callback


3、offGlobalStateChange:

取消 观察者 函数 - 该实例不再响应 globalState 变化。


语法:

offGlobalStateChange: () => boolean //移除当前应用的状态监听,微应用 umount 时会默认调用

4、图解

我们从上图可以看出,我们可以先注册 观察者 到观察者池中,然后通过修改 globalState 可以触发所有的观察者函数,从而达到组件间通信的效果。

二、示例

本示例介绍的是子应用中通过路由守卫,把当前的路由面包屑名称传到主应用中,主应用展示面包屑功能。

主应用
1、在父应用中使用initGlobalState设置全局状态actions并导出供其他组件使用。

actions.js

// 此action文件为定义微应用之间全局状态
// 引入qiankun的应用间通信方法initGlobalState

import { initGlobalState } from 'qiankun';

const initalState = {
    // 这里可以写初始化数据
}

//初始化state
const actions = initGlobalState(initalState)

export default actions;
2、在main.js中引入actions实例并在注册子应用时通过props传递全局状态actions:

main.js

import actions from './micro/actions'
// 注册的应用列表
registerMicroApps([
  {
    name: 'sub2',//应用名字
    entry: '//localhost:8082', //子应用的启用地址
    container: '#app-vue', //子应用所展示的地址 在App.vue中设置id为app-vue
    activeRule: '/sub2', //路由地址
    // 通过props实现通信传递值
    props: {
      actions
    }
  },
  {
    name: 'sub1',//应用名字
    entry: '//localhost:8081', //子应用的启用地址
    container: '#app-vue', //子应用所展示的地址 在App.vue中设置id为app-vue
    activeRule: '/sub1', //路由地址
  },
]);
3、主应用中的组件要修改全局状态actions,就在此组件中引入actions实例

app.vue

<template>
  <div id="app">
    <div class="top"></div>
    <div class="box">
      <div class="left">
        <el-menu :default-active="$route.path" router class="el-menu-vertical-demo" @open="handleOpen"
          @close="handleClose">
          <MenuItem :menu-list="menuList">
          </MenuItem>
        </el-menu>
      </div>
      <div class="right">
        <div class="bread">
          <el-breadcrumb separator-class="el-icon-arrow-right">
            <el-breadcrumb-item v-for="(item,index) in crumb" :key="index">{{item.name}}</el-breadcrumb-item>
          </el-breadcrumb>
        </div>
        <div class="main">
          <router-view></router-view>
          <div id="app-vue"></div>
        </div>
      </div>
    </div>
  </div>
</template>

<script>
import actions from '@/micro/actions'
export default {
  components: {
    MenuItem
  },
  name: "App",
  data() {
    return {
    menuList:[],
    crumb:[]
    };
  },
mounted() {
    //修改全局的actions
    let crumb=[]
    actions.setGlobalState({ crumb })
    console.log("主应用app的actions:",actions)

    //主应用监听参数变化,接受参数,展示面包屑功能
    actions.onGlobalStateChange((state, prevState) => {
      console.log("主应用app观察者,改变前的state为:", prevState);
      console.log("主应用app观察者,改变后的state为:", state);
      if(state?.crumb){
        this.crumb=state.crumb
      }else{
        this.crumb=[]
      }
    })
  }
</script>
子应用

配置子应用的全局状态Actions,子应用中的全局状态必须要跟主应用中的全局状态变量属性名相同,比如主应用中全局状态变量为{token: “main”},则子应用中也需要保证在setGloabalState时也需要设定相同的变量名。

1、在子应用中配置一个空的actions实例为以后重新赋值从主应用中传递过来的actions:

actions.js

function emptyAction() {
    // 警告:提示当前使用的是空 Action
    console.warn("Current execute action is empty!");
}

class Actions {

    // 默认值为空 Action
    actions = {
        onGlobalStateChange: emptyAction,
        setGlobalState: emptyAction
    };
    
    constructor() {
        
    }

    /**
     * 设置 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;
2、然后在mounted的生命周期里注入actions实例:

main.js

import actions from './actions'

function render(props = {}) {
  const { container } = props;

  if(props){
    actions.setActions(props)
  }

  instance = new Vue({
    router,
    render: (h) => h(App),
  }).$mount(container ? container.querySelector('#app') : '#app');//主应用中设置的id
}
3、子应用向主应用发送数据(子应用中修改数据,可以在主应用中监听到)

router/index.js

import Vue from 'vue'
import VueRouter from 'vue-router'
import HomeView from '../views/HomeView.vue'

import actions from '@/actions'

Vue.use(VueRouter)

const routes = [{
    path: "/",
    redirect: "home"
  },
  {
    path: '/home',
    name: 'home',
    component: HomeView,
    meta:{
      fullPath:[{name:'首页'}]
    }
  },
  {
    path: '/about',
    name: 'about',
    component: () => import( /* webpackChunkName: "about" */ '../views/AboutView.vue'),
    meta:{
      fullPath:[{name:'首页'},{name:'关于'}]
    }
  }
]

const router = new VueRouter({
  mode: 'history',
  base: window.__POWERED_BY_QIANKUN__ ? '/sub2/' : '/',
  routes
})

// 通过路由守卫,把当前的路由面包屑名称传到主应用中
router.afterEach(to => {
  console.log("to:", to.meta.fullPath)
  actions.setGlobalState({ crumb: to.meta.fullPath });
})

export default router
4、子应用在mounted钩子函数中注册qiankun的观察者函数

这里也可以监听主应用的参数

mounted(){
    actions.onGlobalStateChange((state) => {
      console.log("我是子应用,我检测到数据了:", state);
    }, true); 
    //onGlobalStateChange的第二个参数设置为true,则会立即触发一次观察者函数
  }


三、效果图

目录
相关文章
|
5月前
|
存储 前端开发 索引
REACT 在组件之间共享状态
REACT 在组件之间共享状态
|
19天前
|
存储 JavaScript 前端开发
Redux 做状态管理和发布订阅模式有什么区别
【10月更文挑战第26天】Redux和发布订阅模式在状态管理方面各有优缺点,它们适用于不同的应用场景和开发需求。在实际开发中,需要根据具体项目的特点和要求,选择合适的状态管理方式,或者在某些情况下将二者结合使用,以达到最佳的开发效果和性能表现。
98 65
|
3月前
|
存储 JavaScript 前端开发
不要滥用Pinia和Redux了!多组件之间交互可以手写一个调度器!
【8月更文挑战第24天】不要滥用Pinia和Redux了!多组件之间交互可以手写一个调度器!
64 2
不要滥用Pinia和Redux了!多组件之间交互可以手写一个调度器!
|
3月前
|
前端开发 JavaScript API
掌握React表单管理的高级技巧:探索Hooks和Context API如何协同工作以简化状态管理与组件通信
【8月更文挑战第31天】在React中管理复杂的表单状态曾是一大挑战,传统上我们可能会依赖如Redux等状态管理库。然而,React Hooks和Context API的引入提供了一种更简洁高效的解决方案。本文将详细介绍如何利用Hooks和Context API来优化React应用中的表单状态管理,通过自定义Hook `useForm` 和 `FormContext` 实现状态的轻松共享与更新,使代码更清晰且易于维护,为开发者带来更高效的开发体验。
44 0
|
3月前
Vue3使用Mitt中央事件总线实现组件之间通讯(发布订阅库)
Vue3使用Mitt中央事件总线实现组件间的发布订阅通信,替代了Vue2中已移除的EventBus。
430 0
|
5月前
|
存储 前端开发 JavaScript
在React中有效地管理组件之间的通信和数据流
在React中有效地管理组件之间的通信和数据流
|
4月前
|
移动开发 JavaScript 前端开发
VUE实现一个列表清单【props 父子组件通信、slot插槽的使用、全局自定义指令的封装、$nextTick解决异步DOM更新、巧用v-model简化父子组件之间的通信、触发事件的事件源event】
VUE实现一个列表清单【props 父子组件通信、slot插槽的使用、全局自定义指令的封装、$nextTick解决异步DOM更新、巧用v-model简化父子组件之间的通信、触发事件的事件源event】
44 0
|
6月前
|
JavaScript 前端开发 API
【前端--Vue】组件之间的多种通信方式,一文彻底搞懂组件通信!
【前端--Vue】组件之间的多种通信方式,一文彻底搞懂组件通信!
【前端--Vue】组件之间的多种通信方式,一文彻底搞懂组件通信!
|
6月前
|
JavaScript
vue父子组件之间通讯方式
vue父子组件之间通讯方式
32 2
|
6月前
|
JavaScript
Vue全局事件任意组件间通信
Vue全局事件任意组件间通信
24 0