这是我参与8月更文挑战的第14天,活动详情查看:8月更文挑战
什么是组件间通信
组件是vue最强大的功能之一,每一个.vue文件都是一个组件。每个组件都是一个组件对象实例,都有自己的上下文、属性和方法,因此组件间的数据是无法共享的。但实际开发工作中我们常常需要让组件之间共享数据,这也是组件通信的目的。
组件间通信的分类
组件间通信的分类有:
- 父子组件通信
- 兄弟组件通信
- 祖孙组件通信
- 非关系组件间通信
组件间通信的方式
props、$emitref组件引用EventBus事件总线\$parent / \$root\$attrs与$\listenersprovide与injectVuex
props、$emit
props用于父向子传递
- 父组件在子组件标签中通过字面量来传递值
- 子组件设置
props属性,定义接收父组件传递过来的参数
// father.vue
<Children name:"jack" age=18 />
// children.vue
props:{
name:String
age:{
type:Number, // 接收的类型为数值
defaule:18, // 默认值为18
require:true // age属性必须传递
}
}
$emit用于子向父传递
- 在子组件中调用
$emit方法通知父组件,第一个参数是自定义事件名,第二个参数是携带的数据 - 父组件绑定监听子组件的自定义事件,获得传递的数据并更新状态
// children.vue
this.$emit('childPush', payload)
// father.vue
<Children @add="handleChildPush($event)" />
ref 组件实例引用
通过在组件标签上绑定ref属性,然后通过this.\$ref获得组件实例的引用,通过这个引用可以直接获得组件的属性和方法。
<Children ref="child" />
this.$refs.child
$parent 、$root
子组件通过\$parent获得父组件实例引用,通过\$root获得根组件引用。
主要用于兄弟组件通信,这是因为兄弟组件之间都有一个共同的祖辈组件,将这个祖辈组件作为兄弟组件通信的桥梁,用来转发数据。
// 兄弟组件1
this.$parent.on('add',this.add)
//兄弟组件2
this.$parent.emit('add')
provide 与 inject
Vue 2.2.0新增,用于祖孙组件通信,允许一个祖先组件向其所有子孙后代注入一个依赖,不论组件层次有多深,子孙组件都能直接拿到祖先组件传递的数据。
Vue.js provide-inject (vuejs.org)
// 父级组件提供 'foo'
var Provider = {
provide: {
foo: 'bar'
},
// ...
}
// 子组件注入 'foo'
var Child = {
inject: ['foo'],
created () {
console.log(this.foo) // => "bar"
}
// ...
}
$attrs与$\listeners
Vue.js (vuejs.org) attrs与listeners
Vue 2.4.0新增,主要用于祖孙组件通信。
$attrs:包含了父作用域中不被prop所识别 (且获取) 的特性绑定 (class和style除外)。当一个组件没有声明任何prop时,这里会包含所有父作用域的绑定 (class和style除外),并且可以通过v-bind="$attrs"传入内部组件。通常配合 interitAttrs 选项一起使用。$listeners:包含了父作用域中的 (不含.native修饰器的)v-on事件监听器。它可以通过v-on="$listeners"传入内部组件
// child:并未在props中声明foo
<p>{{$attrs.foo}}</p>
// parent
<HelloWorld foo="foo"/>
// 给Grandson隔代传值,communication/index.vue
<Child2 msg="lalala" @some-event="onSomeEvent"></Child2>
// Child2做展开
<Grandson v-bind="$attrs" v-on="$listeners"></Grandson>
// GrandChild
<div @click="$emit('some-event', 'msg from grandson')">
{{msg}}
</div>
EventBus事件总线
基于发布/订阅模式,定义一个EventBus事件总线对象,组件间通信通过订阅对方的发布事件来进行。类似于现实中的邮局。主要用于兄弟组件通信。
// 事件总线简易实现
class EventBus {
constructor() {
this._events = new Map() // 存放 <事件-事件回调队列> 的映射
}
// 订阅
$on(name, cb) {
const tasks = this._events.get(name) || []
//如果没有该事件,则进行注册,否则加入到队列中
tasks.push(cb)
this._events.set(name,tasks)
}
//发布
$emit(name, payload) {
//如果该事件存在,则依次执行队列里的回调
const tasks = this._events.get(name)
tasks && tasks.forEach((cb) => cb.call(this,payload));
}
}
// 绑定到Vue对象原型,每个Vue组件都是它的一个实例,因此这样都能访问到事件总线实例
Vue.prototype.$eventBus = new EventBus()
// comp-1.vue
// 组件1订阅组件2的事件
this.$eventBus.$on('comp-2-push',cb)
// comp-2.vue
// 组件2发布事件,并携带数据
this.$eventBus.$emit('comp-2-push', {data:"..."})
Vuex
vuex利用单一状态树来存储共享变量,提供全局统一状态管理,组件间通信的终极解决方案。适用于任何类型的组件通信,但它需要额外引入,因此主要用于复杂关系组件间的数据传递,如路由组件,跨页面组件通信等。
state:用来存放共享变量的集合getter:相当于state的计算属性。mutations:修改state的方法集合,所有修改state的行为必须由mutations中的方法来执行,不管同步还是异步,同步可以通过commit调用,异步修改必须经过action间接调用。actions:用于异步修改state,但不是直接修改,而是在异步回调执行时调用mutations中的方法来修改。
总结
- 父子关系的组件数据传递选择
props与$emit进行传递,也可选择ref - 兄弟关系的组件数据传递可选择
EventBus,其次可以选择$parent进行传递 - 祖先与后代组件数据传递可选择
attrs与listeners或者Provide与Inject - 复杂关系的组件数据传递可以通过
vuex存放共享的变量