JimQing's Blog


View JimQing's projecton GitHub

Vue $on $emit $off

19 Oct 2019

文章简概

​ 通过$on $emit $off 做事件中心,以及其手动实现。

常见的事件绑定例如$emit,在Vue的使用过程中,我们经常在子组件中通过$emit去调用父组件(容器)中的事件。

父组件
<div>
    <child @doSomething="fatherMethod"/>  
</div>
<script>
...
method: {
    fatherMethod() {}
}
...
</script>
子组件
this.$emit('doSomething');

这是一种常见的简单应用,但是面对更多更复杂的场景,并不是显得那么好用,例如父组件下有AB两个子组件,而A组件想要触发B中的事件,则会有一些麻烦。

父组件
<div>
    <child @doSomething="changeChild2Data"/>  		
  	<child2 :someData="someData"/>  
</div>
<script>
...
method: {
    changeChild2Data() {
        // someData Change
    }
}
...
</script>
子组件child2
...
watch: {
    someData(newVal) {
        // if value change
        this.doSomethingNew();
    }
}
...

可见,按照正常的流程会通过父组件做个中转,这样的操作徒增麻烦,同时在代码可读性及维护上带来了很多问题。但是通过Vue的 $on $emit $off 我们可以做一个事件中心(eventHub),对于这些事件做一个统一管理。

$on 可以将一个事件绑定。

$emit 触发绑定的事件

$off 对一个已绑定的事件解绑

Vue.prototype.eventHub = new Vue();
new Vue().$mount('#app')

在入口文件中我们将一个Vue插入到原型中的eventHub中,方便使用。

回到上面的场景,在子组件1中绑定事件,通过eventId标识

组件 Child
...
mounted() {
    this.eventHub.$on('eventId', this.doSomethingNew)
}
...

在子组件2中触发事件,调用this.doSomethingNew()方法;

组件 Child2
...
method: {
    dosomething() {
        this.$eventHub.$emit('eventId');
    }
}
...

通过$off 解绑对应的方法。

...
method: {
    dosomething() {
        this.$eventHub.$off('eventId');
    }
}
...

通过上诉效果,简化了兄弟组件之间方法的调用过程。这就是事件中心的原理,通过一个事件中转站,对不同位置的事件进行一个统一的管理。从而提高了代码的可读性,简化了流程。

手动实现$on
const eventList = {};
const $on = function(eventId, myfun) {
    if(!eventList[eventId]) {
        eventList[eventId] = [];
    }
    eventList[eventId].push(myfun);
}
手动实现$emit
const $emit = function(eventId, data) {
    if (eventList[eventId]) {
        eventList[eventId].forEach(handle => handle(data));
    }
}
手动实现$off
const $off = function(eventId, handle) {
    const index = eventList[eventId].findIndex(fun=> fun===handle);

    if (idnex > -1) {
        eventList[eventId].splice(idnex, 1);
    }
}
完整的eventHub手动实现
const createEventHub = () => ({
    hub: Object.create(null),
    emit(event, data) {
        (this.hub[event] || []).forEach(handler => handler(data));
    },
    on(event, handler) {
        if (!this.hub[event]) this.hub[event] = [];
        this.hub[event].push(handler);
    },
    off(event, handler) {
        const i = (this.hub[event] || []).findIndex(h => h === handler);
        if (i > -1) this.hub[event].splice(i, 1);
    }
});

参考文章:

https://www.html.cn/30-seconds-of-code/#createeventhub (30 秒就能理解的 JavaScript 代码片段)

https://vuejs.org (Vue官方文档)

至此,希望此博客能对你有所帮助。