Vue事件总线(EventBus)$on$emit$off

Posted 奥特曼 

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Vue事件总线(EventBus)$on$emit$off相关的知识,希望对你有一定的参考价值。

在之前我们只用过父传子,子传父进行传数据,这时候当组件嵌套比较深或比较复杂的情况,这时候就用到了事件总线 (EventBus) 

如何理解事件总线呢,你可以理解为用来传输数据的一条线

注意点:有组件发布事件后 剩余的所有组件都可以进行监听事件

一、使用EventBus

1. 创建事件总线   main.js

import Vue from 'vue'
// 创建事件总线   就相当于创建了一个新的vue实例
const bus = new Vue()
// 把bus挂载到了Vue的原型上, 保证所有的组件都能通过 this.$bus访问到事件总线
Vue.prototype.$bus = bus

2. 页面使用 发布事件 - 传递值

// this.$bus.$emit('事件名', 额外参数)
this.$bus.$emit('send', 'hello')

3.订阅事件 - 接收组件值

// 1. 在created中订阅
// 2. 回调函数需要写成箭头函数
// this.$bus.$on('事件名', 事件回调函数)
this.$bus.$on('send', msg => {
  console.log(msg)
})

二、注意点

$on先执行 在执行$emit触发事件  (嵌套关系)

知道此问题必须先要知道父子级组件嵌套关系的钩子函数执行顺序?如果你对生命周期钩子函数不了解先来看 生命周期的详解

父子嵌套生命周期  会先执行父组件中的前三个后 执行子组件的前三个 再去执行父组件的mounted 

接下俩我们做个实验 看看为什么先执行$on 在执行$emit

父组件中

<template>
  <div>
  <son></son>
  </div>
</template>
<script>
import son from './Son.vue'
export default {
  components: {
    son
  },
  created() {
    console.log('父组件:我被创建啦')
    this.$bus.$emit('bus', 10)
  }
}
</script>

子组件

<template>
    <div>
  {{message}}
    </div>
</template>

<script>
export default {
  data () {
    return {
      message: ''
    }
  },
  created () {
    console.log('子组件:我被创建啦')
     this.$bus.$on('bus', (val) => {
       console.log(val)
      this.message = val
     })
  },
}
</script>

这时候是看不见任何接收信息的  

原因就是 没有等子组件$on去注册事件后就执行了父组件$emit 所以接收不到信息

解决方法:把父组件中的$emit事件放在mounted钩子函数中 等待子组件创建并注册$on事件后再去触发$emit。 只适合嵌套关系 可能出现兄弟关系 也要看情况进行使用

  mounted () {
    console.log('父组件:mounted')
    this.$bus.$emit('bus', 10)
  }

三、移除事件监听

来看个栗子 

父组件中没两秒进行一次传递  点击v-if后将子组件进行 销毁或重新创建

<template>
  <button @click="isShow=!isShow"> {{isShow?'销毁':'重建'}}  </button>
  <son v-if="isShow=isShow">son</son>
  </div>
</template>
<script>
import son from './Son.vue'
export default {
  components: {
    son
  },
  data () {
    return {
      isShow: true
    }
  },
   created () {
   console.log('父组件:我被创建啦')
   setInterval(() => {
   this.$bus.$emit('bus', 10)
    }, 2000)
   }
}

子组件 

<template>
    <div>
  {{message}}
    </div>
</template>

<script>
export default {
  data () {
    return {
      message: null
    }
  },
  created () {
    console.log('子组件:我被创建啦')
    this.$bus.$on('bus', (val) => {
      console.log(val)
      this.message = val
    })
  }
}
</script>

问题点:当点击销毁子组件的时候 子组件还能接收到数据吗?对应的回调函数还能在执行吗? 答案是 会,事件订阅功能是$eventBus对象完成的,与组件无关,当你点击销毁后 再点击创建又会多了一个订阅事件,依次类推每次点击新建后都会多一个订阅事件

造成原因:事件订阅是通过$eventBus对象完成的 与组件无关

如果不移除事件监听 并且会造成内存泄漏

在son组件中修改

  created () {
    console.log('子组件:我被创建啦')
    const m = 1 * 1024 * 1024
    const arr = new Array(m).fill('a')

    this.$bus.$on('bus', function f1 (val) {
      // 注意这里有一个闭包
      console.log(val, 'son listen... bus', arr[1])
    })
  },

通过v-if 销毁和重新创建来看通过数据发现 销毁后并没有对空间进行释放

解决方案: 在子组件销毁后进行取消订阅事件

  destroyed () {
    // 取消对bus事件的监听
    this.$bus.$off('bus')
  }

对bus取消事件监听后  内存得到了释放

总结:

1. 任何组件都可以在事件总线中发布事件 this.$bus.$emit('xxx','传递参数') 

2. 任何组件都可以在事件总线中监听事件 this.$bus.$on('xxx',(接收参数)=>{ 对形参进行操作 }) 

3. main.js注册空的Vue对象, 只负责$on注册事件, $emit触发事件, 一定要确保​$on先执行

4.$off的格式:

  • $off() 会取消所有的事件订阅;

  • $off('事件名') 会取消指定事件名的;

  • $off('事件名', 回调) 会取消指定事件名的,指定回调

参考 凡人进阶

以上是关于Vue事件总线(EventBus)$on$emit$off的主要内容,如果未能解决你的问题,请参考以下文章

Vue eventBus 事件总线

Vue eventBus 事件总线

Vue事件总线(EventBus)

用$emit $on实现页面弹窗

vue 事件总线EventBus的概念、使用以及注意点

vue中bus.$on事件被多次绑定