概述
Vue2/3中事件总线 - EventBus 区别以及详细使用方法
- Vue 事件总线
- 事件总线
- Vue2 中事件总线
- Vue3 中事件总线
- 扩展阅读
- 关于provide和inject使用需要注意的是:
- 解决provide和inject的响应式的问题
Vue 事件总线
事件总线本质上还是解决各个组件之间的通信问题。
解决在各个不同层级关系的组件之间传值/通信,目前常见的方法有:
- 项目中有多层组件传参可以使用
$attrs
,可以使代码更加美观,更加简洁,维护代码的时候更方便;
<!-- Father.vue -->
<template>
<!-- 给子组件传了两个参数,但是子组件没有用prop接收-->
<child-comp :is-show="isOpen" :row="row"></child-comp>
</template>
<!-- Child.vue -->
<template>
<div class='child-view'>
<p>子组件</p>
<GrandChild v-bind="$attrs"></GrandChild>
</div>
</template>
<script>
import GrandChild from './GrandChild.vue'
export default {
// 继承所有父组件的内容
inheritAttrs: true,
components: { GrandChild }
}
</script>
<!-- Grand.vue -->
<template>
<div class='grand-child-view'>
<p>孙子组件</p>
<p>传给孙子组件的数据:{{row.name}} {{row.name !== undefined? '学习' : ''}} {{row.study}}</p>
</div>
</template>
<script>
export default {
// 不想继承所有父组件的内容,同时也不在组件根元素dom上显示属性
inheritAttrs: false,
// 在本组件中需要接收从父组件传递过来的数据,注意props里的参数名称不能改变,必须和父组件传递过来的是一样的
props: {
isShow: {
type: Boolean,
dedault: false
},
row: {
type: Object,
dedault: () => { }
}
}
}
</script>
(如果给组件传递的数据,组件不使用 props 接收,那么这些数据将作为组件的HTML元素的特性,这些特性绑定在组件的HTML根元素上)
- 在复杂关系的组件之间,如果使用普通的父子组件传参
prop
、$emit
、$on
会很繁琐,特别是在兄弟组件和祖先-子孙后代组件这种情形下。 - 如果使用
vuex
会大材小用,只是在这几个组件中使用,没必要使用vuex
;而且vuex
是用作数据状态存储的,如果用来作状态的传递,这种state
数据会很冗余。 - 使用依赖注入(
provide
和inject
)。 - 事件总线 EventBus。
事件总线
eventBus
是一个很简单但是也很实用的方案,适合组件跨级传递消息,我也经常在项目中使用这种方式,推荐一下,大家可以去了解一下这种方式。【需要注意的是:使用事件总线 eventBus
,使用不恰当的话,有可能会出现事件多次执行。】
下面是分别在 Vue2 和 Vue3 中实现事件总线。
Vue2 中事件总线
// common/event-bus.js
import Vue from 'vue'
const EventBus = new Vue()
export const EventBridge = {
eventName: {
closeLabel: 'closeLabel',
openLabel: 'openLabel',
showDetailLabel: 'showDetailLabel'
},
on(name, fn) {
EventBus.$on(name, fn)
},
emit(name, data) {
EventBus.$emit(name, data)
},
offOne(name) {
EventBus.$off(name)
},
// 移除所有的时间监听
offAll() {
EventBus.$off()
},
getEventName(key, value) { // value是没有取到值时的默认值
return this.eventName[key] + value
}
}
使用方法:
// a.vue
<script>
import { EventBridger } from '@/common/event-bus'
export default {
methods: {
showDetail(data) {
EventBridger.emit('showDetailLabel', data)
}
}
}
</script>
// b.vue
<script>
import { EventBridger } from '@/common/event-bus'
export default {
mounted() {
EventBridger.on('showDetailLabel', (data) => {
this.handlerShowDetai(data)
})
},
methods: {
handlerShowDetai(data) {
console.log(data)
}
}
}
</script>
【方式二、将事件总线挂载到全局实例app上】
// main.js
Vue.prototype.$EventBus = new Vue()
new Vue({
router,
store,
render: h => h(App)
}).$mount('#app')
使用方法:
// a.vue
this.$EventBus.$emit('abc')
// b.vue
mounted() {
this.$EventBus.$on('abc, (data) => {
console.log(data)
// do your things
})
}
【方式三】
在方式二中,我们创建了空的 Vue 对象用做事件总线并挂载在我们的全局应用属性上。
也许我们还可以更简单一点。
简单场景直接用 this.$root.$emit
和 this.$root.$on
也是一样的,可以少初始化一个 Vue 对象。
Vue3 中事件总线
在 Vue3.0+ 的版本中,Vue 移除了 $on
、$off
和 $once
方法;并且没有提供兼容的功能。详情查看文档的说明
在 Vue3 中,关于事件总线模式可以被替换为使用外部的、实现了事件触发器接口的库,例如 mitt 或 tiny-emitter,这是官方推荐的事件总线替换方案。
// eventBus.js
import emitter from 'tiny-emitter/instance'
export default {
$on: (...args) => emitter.on(...args),
$once: (...args) => emitter.once(...args),
$off: (...args) => emitter.off(...args),
$emit: (...args) => emitter.emit(...args),
}
它提供了与 Vue 2 相同的事件触发器 API。
在绝大多数情况下,不鼓励使用全局的事件总线在组件之间进行通信。虽然在短期内往往是最简单的解决方案,但从长期来看,它维护起来总是令人头疼。根据具体情况来看,有多种事件总线的替代方案:
- Prop 和事件应该是父子组件之间沟通的首选。兄弟节点可以通过它们的父节点通信。
- Provide 和 inject 允许一个组件与它的插槽内容进行通信。这对于总是一起使用的紧密耦合的组件非常有用。
- provide/inject 也能够用于组件之间的远距离通信。它可以帮助避免“prop 逐级透传”,即 prop 需要通过许多层级的组件传递下去,但这些组件本身可能并不需要那些 prop。
- Prop 逐级透传也可以通过重构以使用插槽来避免。如果一个中间组件不需要某些 prop,那么表明它可能存在关注点分离的问题。在该类组件中使用 slot 可以允许父节点直接为它创建内容,因此 prop 可以被直接传递而不需要中间组件的参与。
- 全局状态管理,比如 Vuex。
扩展阅读
关于provide和inject使用需要注意的是:
然而,依赖注入还是有负面影响的。它将你应用程序中的组件与它们当前的组织方式耦合起来,使重构变得更加困难。同时所提供的 property 是非响应式的。这是出于设计的考虑,因为使用它们来创建一个中心化规模化的数据跟使用 $root 做这件事都是不够好的。如果你想要共享的这个 property 是你的应用特有的,而不是通用化的,或者如果你想在祖先组件中更新所提供的数据,那么这意味着你可能需要换用一个像 Vuex 这样真正的状态管理方案了。
解决provide和inject的响应式的问题
默认情况下,provide/inject
绑定并不是响应式的。这一点很尴尬。
但是,在 Vue2 中,我们可以传入了一个可监听的对象,那么其对象的 property
还是可响应的。
什么是可监听的对象?
可监听的响应对象: Array, Object。
或者,在 Vue3 中,我们可以通过传递一个 ref property
或 reactive
对象给 provide
来使其有响应式。
provide() {
return {
todoLength: Vue.computed(() => this.todos.length)
}
}
为了增加 provide
值和 inject
值之间的响应性,我们可以在 provide
值时使用 ref
或 reactive
。
setup() {
const location = ref('North Pole')
const geolocation = reactive({
longitude: 90,
latitude: 135
})
provide('location', location)
provide('geolocation', geolocation)
}
当使用响应式 provide / inject
值时,建议尽可能将对响应式 property
的所有修改限制在定义 provide
的组件内部。
// 提供 provide 的组件
methods: {
updateLocation() {
this.location = 'South Pole'
}
}
然而,有时我们需要在注入数据的组件内部更新 inject
的数据。在这种情况下,我们建议 provide
一个方法来负责改变响应式 property
。
【父组件自己提供一个可以更改属性的方法,provide
出来,子孙组件调用这个方法来修改 property
。】
const updateUserLocation = inject('updateLocation')
最后,如果要确保通过 provide
传递的数据不会被 inject
的组件更改,我们建议对提供者的 property
使用 readonly
。
provide('location', readonly(location))
—————————— 【正文完】——————————
前端学习交流群,想进来面基的,可以加群: 685486827,832485817;
写在最后: 约定优于配置 —— 软件开发的简约原则
——————————【完】——————————
我的:
个人网站: https://neveryu.github.io/neveryu/
Github: https://github.com/Neveryu
新浪微博: https://weibo.com/Neveryu
微信: miracle421354532
更多学习资源请关注我的新浪微博…好吗
最后
以上就是知性自行车为你收集整理的Vue2/3中事件总线 - EventBus 区别以及详细使用方法Vue 事件总线的全部内容,希望文章能够帮你解决Vue2/3中事件总线 - EventBus 区别以及详细使用方法Vue 事件总线所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复