大家好~~
在开始介绍 qiankun
的应用通信之前,我们需要先了解微前端架构如何划分子应用。
在微前端架构中,我们应该按业务划分出对应的子应用,而不是通过功能模块划分子应用。这么做的原因有两个:
- 在微前端架构中,子应用并不是一个模块,而是一个独立的应用,我们将子应用按业务划分可以拥有更好的可维护性和解耦性。
- 子应用应该具备独立运行的能力,应用间频繁的通信会增加应用的复杂度和耦合度。
综上所述,我们应该从业务的角度出发划分各个子应用,尽可能减少应用间的通信,从而简化整个应用,使得我们的微前端架构可以更加灵活可控。
上面的这段话是引自这篇好文,这篇文章中主要介绍了两种主应用和微应用的通信方式
第一种是 qiankun
官方提供的通信方式 - Actions
通信,适合业务划分清晰,比较简单的微前端应用,一般来说使用第一种方案就可以满足大部分的应用场景需求。
第二种是基于 redux
实现的通信方式 - Shared
通信,适合需要跟踪通信状态,子应用具备独立运行能力,较为复杂的微前端应用。
我写这篇博客的主要原因是探究主应用和多个微应用之间如何使用Vuex来进行状态管理,这其实是对于上面通信方式中,第二种方式的补充。
场景
项目现状:
- 主应用是Vue技术栈,使用Vuex进行状态管理
- 多个微应用也是Vue技术栈,并且都可能使用Vuex进行状态管理
想要解决的问题:
- 主应用与微应用的通信
- 微应用之间的通信
通信方式的选择:
-
qiankun
官方提供的通信方式 -Actions
通信。优点:
- 使用简单;
- 官方支持性高;
- 适合通信较少的业务场景;
缺点:
- 子应用独立运行时,需要额外配置无
Actions
时的逻辑; - 子应用需要先了解状态池的细节,再进行通信;
- 由于状态池无法跟踪,通信场景较多时,容易出现状态混乱、维护困难等问题;
-
使用Vuex进行状态管理
优点:
- 子应用无法随意污染主应用的状态池,只能通过主应用暴露的
shared
实例的特定方法操作状态池,从而避免状态池污染产生的问题。 - 子应用将具备独立运行的能力
缺点:
暂时没有想到。。。。
- 子应用无法随意污染主应用的状态池,只能通过主应用暴露的
核心思想
主要就是在做微前端集成时,将主应用的store共享给所有微应用,针对不同微应用可以进行模块化设置,这样使得共享的state更加易于管理。
具体实现(微应用原本就没有使用Vuex
进行状态管理)
step1:主应用向微应用传递store
实例。
registerMicroApps(
[
{
name: "chai-project",
entry: "//localhost:8080",
container: '#yourContainer',
activeRule: "/chaiQiankunTest/ffff",
props: {
store //共享主应用的store实例
}
}
],
{
beforeLoad: [
app => {
console.log("before load", app);
}
], // 挂载前回调
beforeMount: [
app => {
console.log("before mount", app);
}
], // 挂载后回调
afterUnmount: [
app => {
console.log("after unload", app);
}
] // 卸载后回调
}
)
step2:微应用使用主应用共享的store实例
针对第一种情况,就是在入口文件中引入vuex
,并使用该插件,进而在创建vue
实例的时候,传入主应用共享的store
实例。
import Vuex from 'vuex'
Vue.use(Vuex);
function render (props) {
const store = props.store;
// 在 render 中创建 VueRouter,可以保证在卸载微应用时,移除 location 事件监听,防止事件污染
router = new Router({
// 运行在主应用中时,添加路由命名空间 /chaiQiankunTest/ffff
base: window.__POWERED_BY_QIANKUN__ ? 'chaiQiankunTest/ffff' : '/',
mode: 'history',
routes
});
// 挂载应用
instance = new Vue({
router,
store,//主应用共享的store实例
render: (h) => h(App)
}).$mount('#app');
}
step3:验证主应用和微应用之间是否可以完成通信,状态同步。
验证的思路就是
(1)在主应用和微应用的页面上添加改变同一个state
的方法,并且利用computed
实时显示该state
的状态值。
(具体的实现代码这里就不详细给出)
验证用例主要是
(1)点击主应用按钮,修改state
值之后,主应用和微应用的页面上都实时触发computed
属性,展示最新状态值
(2)点击微应用按钮,修改state
值之后,主应用和微应用的页面上都实时触发computed
属性,展示最新状态值
验证的结果
不论是点击主应用的按钮,还是点击微应用的按钮,主应用的computed
属性成功被触发,微应用始终未能正常监听到状态值得改变,computed
属性从未被触发。
这里查阅了蛮多的资料,没能看到有大神对qiankun使用Vuex
的经验总结(这里也发现自己有点过于依赖网络搬砖,有些问题积极思考是能够找到答案的)
step4:bug修复
微应用的computed
始终没有被触发,底层原因就是其依赖属性this.$store.state.count
是非相应式的,这会导致难以触发。
computed: {
childCount: {
get (){
return this.$store.state.count;
}
}
}
Vuex
正常使用的时候,所有的状态值都是响应式的,可以直接用于Vue
页面之中,但是这里是非响应式的,导致这个的原因其实十分简单。这里的store
实例是由主应用传递过来的,store
中的状态对于主应用的Vue
实例而言是亲儿子,是响应式的,在微应用中,虽然可以使用共享store
实例中的commit
方法,但是对于微应用的Vue
实例而言,不是亲儿子,是非响应式的,这样分析之后,解决方案就十分明确:
在微应用中将共享的store实例进行响应式设置,这是Vue现有的API方法Vue.observable(store)
如此处理之后,不论主应用还是微应用中,修改共享store
的state
状态值,在主应用和微应用中都能够实时感知,并对其做出响应的反馈。
具体实现(微应用本身就有自己store
实例)
这种情况主要考虑的是两种方案
-
主应用需要提前得知子应用的
store
内容,将其与主应用的store进行某种融合,这样子应用在集成环境中以及在独立运行时都能够正常运行。缺点:这方方案会导致主应用和子应用相互耦合过强,并且主应用和子应用都要维护一份子应用的
store
,增加工作量,过于笨重。 -
主应用依旧只传递主应用的
store
,子应用的store依旧在使用,也就是在子应用中既可以操作主应用的store
,也可以操作子应用的store
。优点:主应用只需关注需要交互的状态即可,不用关心子应用原本的
store
内容。子应用也只需关注自己的store
内容即可,降低耦合,减轻复杂度。
父子应用store分离的方案实现
这里基于父应用已经共享自己的store
,并且主应用和子应用之间已经能够完成对于主应用的state
状态变化的响应。考虑如何在子应用中使用两个store
,new Vue()
中目前注入的是主应用的store
,那子应用本身的store
如何全局注册呢?仔细思考后,其实很简单,只需要在入口文件中添加如下一行代码:
Vue.prototype.microStore = microStore;
如此一来,在子应用的各个页面都能够通过this.microStore
访问自身的store
。
computed: {
microCount: {
get (){
return this.microStore.state.microCount;
}
}
}
总结
到此,关于在qiankun的集成中,主应用和子应用使用Vuex进行通信的基本使用方法介绍结束了,缺少了子应用和子应用之间的一个实验,大家有兴趣可以试试。
参考
- 基于 qiankun 的微前端最佳实践(图文并茂) - 应用间通信篇
- 目标是最完善的微前端解决方案 - qiankun 2.0
- qiankun官网API指导