EventBus是Vue2中较为常用的组件通信方式,解决了多组件之间通信和数据共享的问题
这里举个较为典型的例子
某个业务页面
<template>
<div>
<!-- 组件A -->
<component-a></component-a>
<!-- 组件B -->
<component-b></component-b>
</div>
</template>
<script>
import EventBus from "./eventBus.js";
export default {
created() {
EventBus.init(); // 初始化
},
beforeDestroy() {
EventBus.destroy(); // 销毁
}
};
</script>
EventBus.js
import Vue from "vue";
export default new Vue({
data() {
return {
loading: false,
listData: null
};
},
methods: {
// 初始化
init() {
this.destroy();
this.loadData();
},
// 销毁
destroy() {
this.loading = false;
this.listData = null;
},
// 获取数
loadData() {
this.loading = true;
// 通过接口获取数据
// ....
this.listData = [/* 业务数据... */];
}
}
});
组件A
<template>
<div v-if="dataLoading">
菊花转...
</div>
<div v-else>
<!-- 业务代码 -->
</div>
</template>
<script>
import EventBus from "./eventBus.js";
export default {
computed: {
dataLoading() {
return EventBus.loading;
},
listData() {
return EventBus.listData;
}
}
};
</script>
观察上诉代码,会发现有下面几个问题
- 只要业务页面打开,那么EventBus就会加载,且业务页面关闭时,EventBus仍会存在,不会随着业务页面关闭而销毁,这样无疑会占用一些内存。
- EventBus的生命周期只会触发一次。
- 每次打开和关闭业务页面时都需要对EventBus进行初始化和销毁。
- 在一些业务场景下,不需要立即创建EventBus,只会在某些特定条件下才会使用到EventBus。
如何解决?
第一、二、三个问题比较好解决,把EventBus用函数的方式创建,然后业务页面用注入的方式提供给子组件。
eventBus.js
业务页面
子组件
但是这种方案有个很大的缺点,仅能父子组件使用,如果是平级组件这种方案就不行了,而且第四个问题是无法解决的
更好的解决方案
封装一个创建惰性加载的EventBus,利用proxy拦截属性访问
createLazyEventBus.js
import Vue from "vue";
const destroyFunctionName = "destroy";
/**
* 创建惰性加载EventBus
* @param {*} config
* @returns
*/
export default function createLazyEventBus(config) {
let instance = null;
// 销毁函数
function destroy() {
instance.$destroy();
instance = null;
}
return new Proxy(
{},
{
get() {
const key = arguments[1];
if (!instance) {
instance = new Vue(config);
}
if (
key === destroyFunctionName &&
!instance.hasOwnProperty(destroyFunctionName)
) {
return destroy;
}
return Reflect.get(instance, key);
}
}
);
}
改造下EventBus.js
eventBus.js
import createLazyEventBus from "vue";
export default createLazyEventBus({
data() {
return {
loading: false,
listData: null
};
},
// 初始化代码可以直接写到生命周期函数里面了,而且是自动调用
created() {
this.loadData();
},
methods: {
// 获取数
loadData() {
this.loading = true;
// 通过接口获取数据
// ....
this.listData = [
/* 业务数据... */
];
}
}
});
业务页面还是最初的方式把EventBus impor到组件内就行了,但是无需再手动调用init了,并且EventBus只有在子组件用到的时候才会被真正初始化! 业务页面
<template>
<div>
<!-- 组件A -->
<component-a></component-a>
<!-- 组件B -->
<component-b></component-b>
</div>
</template>
<script>
import EventBus from "./eventBus.js";
export default {
created() {
// EventBus.init(); 不需要手动调用init了
},
beforeDestroy() {
EventBus.destroy(); // 需要手动销毁
}
};
</script>
子组件
<template>
<div v-if="dataLoading">
菊花转...
</div>
<div v-else>
<!-- 业务代码 -->
</div>
</template>
<script>
import EventBus from "./eventBus.js";
export default {
computed: {
dataLoading() {
return EventBus.loading;
},
listData() {
return EventBus.listData;
}
}
};
</script>
总结
Vue3中已经不需要EventBus这种组件通信的模式了,但是其中的一些理念思想需要我们记住,比如利用EventBus设计系统的事件总线。惰性加载这种设计模式在日常工作中不经常使用,但是它对性能优化有很大的帮助,它的实现方式也有很多,比如本文使用的Proxy,还有闭包或者class的get等等,我们需要根据实际情况灵活选择合适的实现方案。
知识点链接
[MDN] Proxy文档 developer.mozilla.org/zh-CN/docs/…