面试中,经常会问到vue中组件间的通信方式有哪些?
今天我们就来总结下。
vue中组件间的通信方式
方法一、props/$emit
这是我们比较熟悉的方式,主要是父子组件之间的传递方式,父传子使用props,子传父使用$emit.
此方法经常使用,就不罗列代码了
方法二、$emit/$on
这种方法通过一个空的Vue实例作为中央事件总线EventBus(事件中心),用它来触发事件和监听事件,巧妙而轻量地实现了任何组件间的通信,包括父子、兄弟、跨级。当我们的项目比较大时,可以选择更好的状态管理解决方案vuex。
用法如下:
第一步:项目中创建一个js文件(我通常给它取个名字为bus.js),引入vue,创建一个vue实例,导出这个实例,代码如下(一共就两行):
代码语言:javascript复制import Vue from 'Vue'
export default new Vue
第二步:在两个需要通信的两个组件中分别引入这个bus.js
代码语言:javascript复制import Bus from '这里是你引入bus.js的路径' // Bus可自由更换喜欢的名字
第三步:传递数据的组件里通过vue实例方法$emit发送事件名称和需要传递的数据。(发送数据组件)
代码语言:javascript复制Bus.$emit('click',data)
// 这个click是一个自定义的事件名称,data就是你要传递的数据
第四步:被传递数据的组件内通过vue实例方法$on监听到事件和接受到数据。(接收数据的组件)这里通常挂载监听在vue生命周期created和mounted当中的一个,具体使用场景需要具体的分析,这里不说这个。
代码语言:javascript复制Bus.$on('click',target => {
console.log(target)
// 注意:发送和监听的事件名称必须一致,target就是获取的数据,可以不写target。只要你喜欢叫什么都可以(当然了,这一定要符合形参变量的命名规范)
})
通过以上的四步其实就已经实现了最简单的eventbus的实际应用了。
但是到这儿后,一定要注意一个最容易忽视,又必然不能忘记的东西,那就是清除事件总线eventBus.不手动清除,它是一直会存在的
第五步:在vue生命周期beforeDestroy或者destroyed中用vue实例的$off方法清除eventBus
代码语言:javascript复制beforeDestroy(){
bus.$off('click')
}
方法三、vuex (自己查阅具体方法)
方法四、$attrs/$listeners(自己查阅具体方法)
多级组件嵌套需要传递数据时,通常使用的方法是通过vuex。但如果仅仅是传递数据,而不做中间处理,使用 vuex 处理,未免有点大材小用。为此Vue2.4 版本提供了另一种方法----$attrs/$listeners
$attrs:包含了父作用域中不被 prop 所识别 (且获取) 的特性绑定 (class 和 style 除外)。当一个组件没有声明任何 prop 时,这里会包含所有父作用域的绑定 (class 和 style 除外),并且可以通过 v-bind="$attrs" 传入内部组件。通常配合 interitAttrs 选项一起使用。
$listeners:包含了父作用域中的 (不含 .native 修饰器的) v-on 事件监听器。它可以通过 v-on="$listeners" 传入内部组件
方法五、provide/inject
Vue2.2.0新增API,这对选项需要一起使用,以允许一个祖先组件向其所有子孙后代注入一个依赖,不论组件层次有多深,并在起上下游关系成立的时间里始终生效。一言而蔽之:祖先组件中通过provider来提供变量,然后在子孙组件中通过inject来注入变量。
provide / inject API 主要解决了跨级组件间的通信问题,不过它的使用场景,主要是子组件获取上级组件的状态,跨级组件间建立了一种主动提供与依赖注入的关系。
举个例子
假设有两个组件:A.vue 和 B.vue,B 是 A 的子组件
// A.vue
代码语言:javascript复制export default {
provide: {
name: '浪里行舟'
}
}
// B.vue
代码语言:javascript复制export default {
inject: ['name'],
mounted () {
console.log(this.name); // 浪里行舟
}
}
方法六、$parent / $children与 ref
ref:如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素;如果用在子组件上,引用就指向组件实例
$parent / $children:访问父 / 子实例
需要注意的是:这两种都是直接得到组件实例,使用后可以直接调用组件的方法或访问数据。我们先来看个用 ref来访问组件的例子:
// component-a 子组件
代码语言:javascript复制export default {
data () {
return {
title: 'Vue.js'
}
},
methods: {
sayHello () {
window.alert('Hello');
}
}
}
// 父组件
代码语言:javascript复制<template>
<component-a ref="comA"></component-a>
</template>
<script>
export default {
mounted () {
const comA = this.$refs.comA;
console.log(comA.title); // Vue.js
comA.sayHello(); // 弹窗
}
}
</script>
不过,这两种方法的弊端是,无法在跨级或兄弟间通信。
总结
常见使用场景可以分为三类:
父子通信:
父向子传递数据是通过 props,子向父是通过 events($emit);通过父链 / 子链也可以通信($parent / $children);ref 也可以访问组件实例;provide / inject API;$attrs/$listeners
兄弟通信:
Bus;Vuex
跨级通信:
Bus;Vuex;provide / inject API、 $attrs/$listeners