简易使⽤Vue中的中央事件总线(eventBus)
前⾔
正如我们所知Vue中组件的通信场景很多, 什么跨多级⽗⼦通信, 兄弟通信等等。各种各样的解决⽅法⽹上⽅法都有以及多得飞起, 我就不复制黏贴, 省点ATP。在Vue⽣态中已经有很好的⽅案解决各种疑难杂症的通信问题, 针对中⼤型项⽬, ⾸选Vuex (⽤过都说好),但是如果是⼩型项⽬使⽤Vue的eventBus, 是⼀个不错的选择。我们接下来简单说下Vue的事件总线的使⽤和⼀些常见的问题。
传奇服务器使⽤⽅法
Vue的实例就是⼀个事件总线:
import Vue from 'vue';
var vm = new Vue({
mounted () {
## 此时就可以监听⼀个⾃定义事件event1
this.$on('event1', (data) => {
consolo.log(data);
});
},
methods: {
trigger () {
this.$emit('event1', '猴赛雷~');
}
}
});
on⼀个事件,在⼀些操作下
看得出eventBus的使⽤就是在⼀个组件内通过emit事件, 这个看起来跟订阅发布是⼀回事。
但是这⾥的eventBus是局部的, 只能在这个组件内⽤, 如果在⼀个⽗组件A监听了事件, 在B⼦组件去emit, 是没有反应的,
因为⼦组件B⾃⼰⽊有监听该事件。
<A>
<B></B>
</A>
A.vue⽂件:
export default {
created () {
this.$on('你是不是嘤嘤怪', (嘤) => {
if (嘤.length >= 3) {
console.log('你就是嘤嘤怪');
}
})
}
}
B.vue⽂件:
export default {
mounted () {
this.$emit('你是不是嘤嘤怪', '嘤嘤嘤嘤');
}
}
实现⼀个全局的eventBus
全局的eventBus简单理解为在⼀个⽂件创建⼀个新的vue实例然后暴露出去,
使⽤的时候import这个模块进来即可。我们来编写下这个⽂件:
在项⽬中新增⼀个⽂件eventBus.js, 代码实现如下:
在项⽬中新增⼀个⽂件eventBus.js, 代码实现如下:
火箭发动机import Vue from 'vue';
const Bus = new Vue();
const eventBus = {
TYPES: { // 'TYPES'
EVENT1: { // 'TYPES.EVENT1'
EDIT: { // 'TYPES.EVENT1.EDIT'
INVOKE: {},
CANCEL: {}, // 'TYPES.EVENT1.EDIT.CANCEL'
CONFIRM: {}
},
ADD: {
INVOKE: {},
CANCEL: {},
CONFIRM: {}
},
},
EVENT2: {
EDIT: {
INVOKE: {},
CANCEL: {},
重楼种植方法CONFIRM: {}
},
DELETE: {
INVOKE: {},
CANCEL: {},
CONFIRM: {}
},
}
},
// 注册事件函数
on (eventType, cb = () => {}) {
Bus.$String(), (...args) => {
cb(...args);
});
},
// 触发事件函数
emit (eventType, data) {
Bus.$String(), data);
},
// 销毁注册事件函数
off (eventType) {
Bus.$String());
},
// 注册事件触发⼀次后销毁函数
once (eventType, cb = () => {}) {
Bus.$String(), (...args) => {
cb(...args);
eventBus.String());
});
}
};
(function (typeRoot) {
/**
* @param {*} source 要给每个节点添加链的对象
* @param {*} parentNode 当前节点的链⽐如 EVENT1.EDIT.CANCEL */
function addNodeChain(source, parentNode = 'TYPES') {
const isObj = typeof source === 'object';
if (!isObj) return; // ⽀持传⼊默认的字符串⽅式
const parator = !!parentNode ? '.' : '';
const isObjEmpty = Object.keys(source).length === 0;
if (isObjEmpty) {
source['nodeChain'] = parentNode;
source['nodeChain'] = parentNode;
return parentNode;
}
return;
}
for (const key in source) {
if (source.hasOwnProperty(key)) {
source['nodeChain'] = parentNode;
return parentNode;
}
const nodeChain = parentNode + parator + key;
addNodeChain(source[key], nodeChain);
}
}
}
addNodeChain(typeRoot);
Object.freeze(eventBus);
window.eventBus = eventBus;
})(eventBus.TYPES);
export default eventBus;
上⾯的定义了⼀个eventBus对象,⾥⾯定义以下五个属性:
TYPES (预先定义好的⼀些事件)
on(监听(订阅)事件函数)
emit(触发(发布)事件函数
once(只监听(订阅)⼀次事件函数
off(移除事件)
上⾯定义好的eventBus对象很好理解,⽆⾮就是简单封装了下Bus的⼀些api, 我们来说说TYPES对象和addNodeChain⽅法。TYPES:
因为on⽅法第⼀个传⼊的参数是字符串, 也是事件名字, 如果订阅事件多了, 在项⽬中必然会存在不少的字符串,
如果不⼩⼼写错事件名(多写少些个字母啥的~), Vue并不会给你报错, 这对错误跟踪和定位都是⽐较困难的,
因此我们最好有⼀个专门的⽂件来管理和维护这些事件名。这⾥使⽤对象嵌套的⽅式来事先定义事件名称, 使⽤的时候⽐如(eventBus.TYPES.EVENT1.UPDATE.INVOKE),
因为事先没有定义eventBus.TYPES.EVENT1.UPDATE, undefined没有INVOKE属性, 控制台直接报错,
这样做的好处在⼀定情况避免了上⾯所说的问题。
更年期一般是多少岁
addNodeChain:
这个⽅法是遍历整个对象, 给每个节点添加nodeChain属性, 也是当前节点的到根节点的链。 并且重写了当前节点的toString⽅法,返回当前节点的nodeChain, 我们来打印⼀下执⾏addNodeChain后eventBus.TYPES的结构。
{EVENT1: {…}, EVENT2: {…}, nodeChain: "TYPES", toString: ƒ}
EVENT1: {
ADD: {
CANCEL: {
nodeChain: "TYPES.EVENT1.ADD.CANCEL",
toString: ƒ (),怎样做螃蟹
},
CONFIRM: {nodeChain: "TYPES.EVENT1.ADD.CONFIRM", toString: ƒ},
INVOKE: {nodeChain: "TYPES.EVENT1.ADD.INVOKE", toString: ƒ},
nodeChain: "TYPES.EVENT1.ADD",
toString: ƒ (),
},
EDIT: {INVOKE: {…}, CANCEL: {…}, CONFIRM: {…}, nodeChain: "TYPES.EVENT1.EDIT", toString: ƒ} nodeChain: "TYPES.EVENT1",
toString: ƒ (),
},
EVENT2: {EDIT: {…}, ADD: {…}, nodeChain: "TYPES.EVENT2", toString: ƒ}
nodeChain: "TYPES",
toString: ƒ (),
__proto__: Object,
⽽在定义事件⽅法on时:
on (eventType, cb = () => {}) {
Bus.$String(), (...args) => {
cb(...args);
});
},
上⾯会把接受进来的参数调⽤toString⽅法,传⼊给Bus.$on, 因此事件名称还是字符串。
⾄此, 我们的eventBus就可以⽤了。
验证我们的eventBus
我们验证常见的两个场景:
⽗⼦组件通信
兄弟组件通信
有以下⽗组件A, 和⼦组件B, C, 其中B, C是兄弟组件:
A.vue:
<template>
<div>
<h1>这个是⽗组件</h1>
<B></B>
<C></C>
</div>
</template>
<script>
import B from './B';
import C from './C';
import eventBus from '@/util/eventBus.js';
export default {
created () {
(this.eventBus.TYPES.EVENT1.ADD.CONFIRM, (data) => {
console.log(data);
})
比较级的用法
},
components: {
B,
二手房按揭贷款流程C,
}
}
</script>
--------------------------------------------------------------------------------------------
B.vue:
朱顶红花期<template>
<div>
<h3>这个是⼦组件B</h3>
<button @click="emitParentEvent">点击触发⽗组件A事件</button>
<button @click="emitBrotherEvent">点击触发兄弟组件C事件</button>
</div>
</template>
<script>
import eventBus from '@/util/eventBus.js';
export default {
methods: {
emitParentEvent () {
emitBrotherEvent () {
}
}
</script>
--------------------------------------------------------------------------------------------
C.vue:
<template>
<div>
<h3>这个是⼦组件C</h3>
</div>
</template>
<script>
import eventBus from '@/util/eventBus.js';
export default {
created () {
<(eventBus.TYPES.EVENT1.ADD.INVOKE, (data) => {
console.log(data);
})
},
}
</script>