Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

【搬迁】【踩坑】Vue-eventBus异常触发事件情形 #3

Open
escawn opened this issue Nov 15, 2017 · 1 comment
Open

【搬迁】【踩坑】Vue-eventBus异常触发事件情形 #3

escawn opened this issue Nov 15, 2017 · 1 comment
Labels

Comments

@escawn
Copy link
Owner

escawn commented Nov 15, 2017

  • 本文首发于https://escawn.github.io/(已废弃)
  • 发表时间2017/07/20

eventBus是一个vue实例,上面挂载了许多监听事件,通过触发相应的监听事件,可以实现不同层级的组件之间的通信。

eventBus简介

eventBus是为小型项目等组件层级不深,状态不复杂的项目提供的组件通信方案,它本质上可以看做是一个挂载了许多事件的vue实例,在所需组件中引入这个实例,以此为桥梁,触发不同层级组件中监听的事件。

基础用法

单独的eventBus文件
// 单独声明一个eventBus.js,里面仅需声明一个vue实例
// eventBus.js
import vue from "vue"

const eventBus = new Vue({})
export default eventBus

// componentA
import eventBus from './eventBus.js'
export default {
	// 此处省略n行代码
	methods: {
		func(params) {
			console.log('组件A的被触发了,' + '参数为:' + params)
		}
	},
	mounted: function(){
		this.$nextTick(function(){
			eventBus.$on('message', (params) => this.func(params))
		})
	}
}

// componentB
import eventBus from './eventBus.js'
export default {
	// 在组件B mounted时触发事件A中事件
	mounted: function(){
		this.$nextTick(function(){
			eventBus.$emit('message', ('hhhhhh'))
		})
	}
}

// 组件A的message被触发了,参数为:hhhhh
组件中声明
// componentA
<script>
import Vue from 'vue'
export default {
	data() {
		return {
			eventBus: new Vue({})
		}
	},
	methods: {
		func(params) {
			console.log('组件A的被触发了,' + '参数为:' + params)
		}
	},
	mounted: function(){
		this.nextTick(function(){
			this.eventBus.$on('message', (params) => this.func(params))
		})
	}
}
</script>
<template>
<!-- 此处省略n行代码 -->
<component-b :event-bus="eventBus"></component-b>
</template>

// componentB
export default {
	props: {
		eventBus: {
			type: Object
		}
	},
	// 在组件B mounted时触发事件A中事件
	mounted: function(){
		this.$nextTick(function(){
			this.eventBus.$emit('message', ('hhhhhh'))
		})
	}
}

可以看到,在组件中创建eventBus的方式适用于有直接嵌套关系,并且层级不深的组件,比如父子组件(但是明显父子组件之间有更加便捷的通信方式)。一旦涉及层级较深,事件较多的情形(但是又没复杂到需要使用vuex的地步),我们会采用单文件eventBus直接引入的方式。这样的方式会带来一些隐藏的陷阱,接下来会一一提到。

官方文档 >

异常触发事件

文档结构&场景

文档结构
.
├── componentA.vue
├── componentB.vue
└── eventBus.js
路由
routes: [
    {
      path: '/componentA',
      name: 'componentA',
      component: componentA
    },
    {
      path: '/',
      redirect: { path: '/componentA' }
    },
    {
      path: '/componentB',
      name: 'componentB',
      component: componentB
    }
  ]

行为描述

  • 声明一个单文件的vue实例 eventBus
  • 分别在组件A和组件B里导入eventBus
  • 在组件Amounted时,在eventBus上$on一个message事件
  • 在组件B内设置一个按钮,点击可以$emiteventBus上的message事件
  • 在两个组件的beforeDestroy的声明周期内打出相应讯息
期望结果

在组件A和组件B之间通过路由切换时,点击组件B内按钮,正常触发message事件

实际结果

运行结果
说明:在第二次从组件A切换到组件B时,点击按钮,出现了两次'message'事件被触发的效果,当组件A第N次切换到组件B时,点击按钮,出现了N次'message'事件被触发的效果

推测

组件的销毁不能使eventBus里的事件销毁。并且当组件再次创建时,同名事件不覆盖,会被再次注册绑定。导致看起来是多次触发了同一事件,实际上是同时触发了多个同名事件。

实验

解绑事件

做法:在组件A的beforeDestroy周期,解绑事件

beforeDestroy: function(){
	eventBus.$off('message')
}

结果:达到期望结果
解决推测:组件销毁时事件没被销毁

非全局eventBus使用

做法:增加一个父组件componentParent,在父组件中声明eventBus,并以prop方式传递给组件A和组件B,在组件A销毁时销毁
结果:达到期望结果
解决推测:事件不销毁是挂载的eventBus没被销毁导致的

同名事件

做法:在组件A内绑定一个同名事件message

// componentA
mounted: function(){
	this.$nextTick(function(){
		eventBus.$on('message', (params) => this.func(params))
		);
		eventBus.$on('message', () => console.log('再次绑定了message事件'));
	})
}

结果:点击按钮时,console.log两次,说明两个事件都被触发了。
解决推测:同名事件不会被覆盖

keep-alive的影响

做法:在组件A和组件B的<router-view>前增加<keep-alive>标签,保持组件在路由切换时不被销毁
结果:达到期望结果
解决推测:组件销毁-创建会重复注册事件

总结

由以上实践可知

  • 全局(文件式)eventBus上的事件不会随着组件的销毁而销毁
  • eventBus允许同名事件存在
  • 组件被重新创建会再次绑定注册事件

反思

事件解绑是必要的,之前写代码没有这方面的意识,这次踩坑发现了,写代码要严谨,考虑清楚在组件的各个声明周期内要做什么。

@escawn escawn added the vue label Nov 15, 2017
@myzcid
Copy link

myzcid commented Mar 16, 2020

我也遇到了

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants