vue复习,组件间通信方式有哪些

983 阅读3分钟

这是我参与8月更文挑战的第14天,活动详情查看:8月更文挑战

什么是组件间通信

组件是vue最强大的功能之一,每一个.vue文件都是一个组件。每个组件都是一个组件对象实例,都有自己的上下文、属性和方法,因此组件间的数据是无法共享的。但实际开发工作中我们常常需要让组件之间共享数据,这也是组件通信的目的。

组件间通信的分类

vue组件通信类型

组件间通信的分类有:

  • 父子组件通信
  • 兄弟组件通信
  • 祖孙组件通信
  • 非关系组件间通信

组件间通信的方式

  • props、$emit
  • ref组件引用
  • EventBus事件总线
  • \$parent / \$root
  • \$attrs$\listeners
  • provideinject
  • Vuex

props、$emit

props用于父向子传递

  1. 父组件在子组件标签中通过字面量来传递值
  2. 子组件设置props属性,定义接收父组件传递过来的参数
// father.vue
<Children name:"jack" age=18 />

// children.vue
props:{
 name:String 
 age:{  
    type:Number, // 接收的类型为数值
    defaule:18,  // 默认值为18
    require:true // age属性必须传递
 }
}

$emit用于子向父传递

  1. 在子组件中调用$emit方法通知父组件,第一个参数是自定义事件名,第二个参数是携带的数据
  2. 父组件绑定监听子组件的自定义事件,获得传递的数据并更新状态
// 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 所识别 (且获取) 的特性绑定 (classstyle 除外)。当一个组件没有声明任何 prop 时,这里会包含所有父作用域的绑定 (classstyle 除外),并且可以通过 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利用单一状态树来存储共享变量,提供全局统一状态管理,组件间通信的终极解决方案。适用于任何类型的组件通信,但它需要额外引入,因此主要用于复杂关系组件间的数据传递,如路由组件,跨页面组件通信等。

vuex

  • state:用来存放共享变量的集合
  • getter:相当于state的计算属性。
  • mutations:修改state的方法集合,所有修改state的行为必须由mutations中的方法来执行,不管同步还是异步,同步可以通过commit调用,异步修改必须经过action间接调用。
  • actions:用于异步修改state,但不是直接修改,而是在异步回调执行时调用mutations中的方法来修改。

总结

  • 父子关系的组件数据传递选择props$emit进行传递,也可选择ref
  • 兄弟关系的组件数据传递可选择EventBus,其次可以选择$parent进行传递
  • 祖先与后代组件数据传递可选择attrslisteners或者 ProvideInject
  • 复杂关系的组件数据传递可以通过vuex存放共享的变量