美文网首页VueWeb前端之路让前端飞
vue中eventbus被多次触发(vue中使用eventbus

vue中eventbus被多次触发(vue中使用eventbus

作者: Katherine的小世界 | 来源:发表于2017-09-26 19:18 被阅读7871次

一开始的需求是这样子的,我为了实现两个页面组件之间的数据传递,假设我有页面A,点击页面A上的某一个按钮之后,页面会自动跳转到页面B,同时我希望将页面A上的某一些参数携带过去给页面B。(我知道,小参数的时候可以通过路由的params或者query去传参数,或者大型数据可以用vuex来处理,很遗憾我到现在还没有做很大型的项目,所以还没有用过vuex,接下来会学习一下。)

然后我就想,这不就是不同组件之间的数据传递问题而已吗?直接用bus 巴士事件来传递数据不就行了吗。于是,我就很愉快地进行了。关于vue中的eventbus的使用,我之前在一篇vue中的数据传递中有提到过。 。

先给你们看一下我一开始的代码:

实现目标: 
点击之后,bus emit事件,然后顺便跳转路由到/moneyRecord页面。
接下来就是在MoneyRecord页面中去on接收这个事件,然后接受参数。
// 这是页面A的内部触发bus事件的代码
  editList (index, date, item) {
//  点击进入编辑的页面,需要传递的参数比较多。
      console.log(index, date, item)
      bus.$emit('get', {
        item: item.type,
        date: date
      })
      this.$router.replace({path: '/moneyRecord'})
    }

// moneyRecord页面
created () {
    //这里我将icon的list给保存下来了
    bus.$on('get', this.myhandle)
  },
methods: {
  myhandle (val) {
      console.log(val, '这是从上个页面传递过来的参数')
    }
}

就当我欣喜若狂的时候,觉得自己只要在页面A触发了get事件,页面B中就会理所当然的接受了数据。然而,结果却不如人意,看一下下面的动图。

主要是看“”这是从上个页面传来的数据这一行数据的输出次数情况来判断事件触发次数。“”

test.gif

不知道你有没有发现,就是我第一次进去list页面的时候,我随便点击一下list下的任何一个item,控制台没有输出。但是当我第二次再点击触发事件的时候,就会输出一个测试数据。再一次进去点击,就输出两个数据。。。依次增加了。(控制台上那个“这是从上个页面传来的数据”就是测试数据)

所以,有两个问题。

问题

  • 问题1: 为什么第一次触发的时候页面B中的on事件没有被触发
  • 问题2: 为什么后面再一次依次去触发的时候会出现,每一次都会发现好像之前的on事件分发都没有被撤销一样,导致每一次的事件触发执行越来越多。

解决

针对问题1
这个还得从vue的生命周期说起了,我先进行了测试,就是当从页面组件A跳转到页面组件B的时候,两个组件的生命周期分别是怎么样的,关于vue的生命周期具体每一个时期做什么事情我就不再赘述了,下面po一张vue生命周期的图。

image.png

我自己做了实验来验证,这个页面跳转过程中,这两个组件的生命周期的执行情况。

// 我分别在页面A和页面B中去添加以下代码:
beforeCreate () {
   console.group('%c%s', 'color:red', 'beforeCreate 创建前状态===============组件2》')
 },
 created () {
   console.group('%c%s', 'color:red', 'created 创建完毕状态===============组件2》')
 },
 beforeMount () {
   console.group('%c%s', 'color:red', 'beforeMount 挂载前状态===============组件2》')
 },
 mounted () {
   console.group('%c%s', 'color:red', 'mounted 挂载状态===============组件2》')
 },
 beforeUpdate () {
   console.group('%c%s', 'color:red', 'beforeUpdate 更新前状态===============组件2》')
 },
 updated () {
   console.group('%c%s', 'color:red', 'updated 更新状态===============组件2》')
 },
 beforeDestroy () {
   console.group('%c%s', 'color:red', 'beforeDestroy 破前状态===============组件2》')
 },
 destroyed () {
   console.group('%c%s', 'color:red', 'destroyed 破坏状态===============组件2》')
 }
// 另外一个组件的我就不放出来了

测试结果图:

test.gif image.png

其实,可以通过结果清楚看到,当我们还在页面A的时候,页面B还没生成,也就是页面B中的 created中所监听的来自于A中的事件还没有被触发。这个时候当你A中emit事件的时候,B其实是没有监听到的。

再看一下,红色的是B页面组件,当你从页面A到页面B跳转的时候,发生了什么?首先是先B组件先created然后beforeMount接着A组件才被销毁,A组件才执行beforeDestory,以及destoryed.

所以,我们可以把A页面组件中的emit事件写在beforeDestory中去。因为这个时候,B页面组件已经被created了,也就是我们写的$on事件已经触发了

所以可以,在beforeDestory的时候,$emit事件

// 修改一下A页面中的代码:
// 这是原先的代码
  editList (index, date, item) {
//  点击进入编辑的页面,需要传递的参数比较多。
      console.log(index, date, item)
      this.item = item.type
      this.date = date
      this.$router.replace({path: '/moneyRecord'})
    }
// 重新在data属性内部定义新的变量,来存储要传过去的数据;
然后:
 beforeDestroy () {
 console.log(this.highlight, '这是今年的数据', this, '看看组件销毁之前会发生什么')
 bus.$emit('get', {
        item: this.item,
        date: this.date
      })
 },

接下来。看一下修改之后的效果

test.gif

可以看到,就是第一次点击list的时候,也就是第一次触发emit事件的时候,控制太就输出了,所以在beforeDestoryed去$emit是起到作用的,B页面组件也监听$on到了。

但是,好像,就是事件的触发还是会依次增加,就是控制台的输出每次都有所增加了。。。

解决:
看一下github上提出的。issue
https://github.com/vuejs/vue/issues/3399

image.png

尤大大提出了以下解决:

image.png

*就是说,这个$on事件是不会自动清楚销毁的,需要我们手动来销毁。(不过我不太清楚这里的external bus 是什么意思,有大神能解答一下的吗,尤大大也提到如果是注册的是external bus 的时候需要清除)****

所以。我在B组件页面中添加Bus.$off来关闭。代码如下:

// 在B组件页面中添加以下语句,在组件beforeDestory的时候销毁。
  beforeDestroy () {
    bus.$off('get', this.myhandle)
  },

来看一下输出的结果

t可以看到,控制台第一次进去的时候就有输出,而且输出的不会逐次增加

*当然,尤大大还说可以写一个mixin?我还不知道是什么?以后在研究一下。

总结: 所以,如果想要用bus 来进行页面组件之间的数据传递,需要注意亮点,组件A$emit事件应在beforeDestory生命周期内。其次,组件B内的$on记得要销毁。

提问时间:你们在实现页面组件之间的数据传递有什么好的方法吗?可以留言分享一下吗?有时候虽然也可以通过从后台获取,但是考虑到数据只有几个需要传的话,就没有必要去请求数据,我知道有的还有用vueX传递。还有呢?

相关文章

网友评论

  • 6f5892321a2e:用于组件间传递数据还可以,但是跳页面的还是不要用吧,用户一刷新你的数据都没了。
  • 0c45406e8da8:文章内容挺不错的。我们侠课岛工作是远程录制课程视频或图文教程,我们会给到课程的需求大纲,每一节课程需要你来详细展开写一些代码举例和讲解清楚,对经验积累和创新能力有一定的要求,有兴趣可以联系我。加我微信:zhimadt
  • pruple_Boy:赞作者的分享精神,我目前理解的 Vue: 1. 事件流 :组件间
    1. 事件$emit和$event:
    2. 新增$attrs和$listeners:
    2. 中央事件总线:建议不复杂的页面间
    1. Bus 实现方法
    1. 通过 $root 实现:在 App.vue 内创建一个 Bus 属性空 Vue 对象,通过 $root 访问
    2. 依赖注入 provide 和 inject 一个空的 Vue 对象
    3. 新建一个 Bus.vue文件, new 一个 vue 实例导出,相当于是添加一个传话员
    4. 使用Bus库
    2. 页面间使用Bus
    1. 注意使用后清空,避免重复调用。— $once 能确保仅仅会被调用一次,优于 $on
    2. 尤其需要注意,组件A $emit事件应在beforeDestory生命周期内:避免出现第一次监听不到;下次点击监听两次的情况(已处理 $off )— 监听两次可使用 $once
    3. Vuex状态管理机
    1. 简单情况可使用事件总线
    4. 其他方式
    1. 边界情况的 $root/$parent/$children
    2. 依赖注入 provide 和 inject
    3. vux 提供的 vue 全局公用函数
    4. 路由的 params 和 query 传参
  • 8d2855a6c5d0:手动点赞啊~
  • 骑着蜗牛追飞机_f7fa:很强。第二个问题你的解决方案又给了我思路,,我还试了试 $once ,貌似也可以,只是不知道为什么第一次的时候,是console两次,之后就正常了。
    不过觉得你挺厉害的哈,很棒。
  • 代码论斤称:楼主的解决方案很棒,但是要记得两个页面都要写: beforeDestroy () {
    bus.$off('get', this.myhandle)
    },
    49b5a372bc91:@Katherine的小世界 看到mixin 恍然大悟 因为都要调用beforeDestroy 在main.js 在每个mixin一下这个钩子在每个组件就都会默认调用了
  • 1ab30e040b01:感谢分享!!,我想知道什么情况下就会发生这样的情况,跟组件注册前后顺序有关系么
  • 向sunshineLife看齐:感谢楼主分享,对我帮助很大!第一种情况放$emit到组件的$nextTick队列中也可以解决
  • ShenglongWang:第一种情况用这个方法为什么我用这个方法没有用?beforeDestory这个方法呀在哪里调用?
    世界很大:很用心的内容,感谢作者。。
    Katherine的小世界:@AlongeGe beforedestoryed是生命周期里的其中一个 组件销毁之前会被调用 你想在组件被销毁之前做一些操作 可以定义在里面
  • agrass:第一次路由到页面2,this.$nextTick 可以避免第一个问题吧。
  • baa0ac740b1f:确实有用
  • encumbranc_c9e0:可以使用vm.$once( event, callback )吧
  • 27a53bc32690:请问楼主....$on监听事件回调函数里面改变某个data值,可以实现,但是出来这个函数后值还是原来的并没有改变,,是什么原因呢?
    Katherine的小世界:@27a53bc32690 什意思呢?我没懂您的问题:no_mouth:
  • xxx_xyy:非常感谢 这问题困扰我1天
    Katherine的小世界:嗯嗯 不用谢哈哈哈哈
  • 程序员卡诺:写的很不错!正在解决同样的问题,你的文章帮我省了不少时间。

    关于问题:
    之前查到还有通过父组件通讯的。。。不过似乎比较麻烦,在用vuex,并没有深究

    最后:
    我认为vuex比较适合多个页面读取同一组数据,如果是传递数据的话似乎并不合适。。。
    比如购物车这种东西就适合vuex,但是如果是结账时选择优惠券的话(跳到优惠券页面再回来),eventbus会更灵活
    Katherine的小世界:嗯嗯 vuex我觉得还是大型应用可能会用到 我目前还没有用到 就还没有去深入学习呢 找时间也看看
  • MrDream:我也遇到过同样的情况,第二种情况也像你一样解决了。第一种情况的解决方案让我又涨了姿势,很棒!!

本文标题:vue中eventbus被多次触发(vue中使用eventbus

本文链接:https://www.haomeiwen.com/subject/dpasextx.html