Vue组件通信方式

209 阅读2分钟

这篇文章希望从整体上认识Vue的几种通信方式,希望做到快速全面掌握。

一、父子组件-props/$emit

这种通信方式很常用,也很基础,大家都很熟悉。不展示代码,仅总结用法:

父组件->子组件: 父组件 v-bind:绑定变量传给子组件。子组件通过props接收数据。

子组件->父组件: 子组件通过emit提交,向父组件传值,父组件通过v-on绑定事件接收数据。

   this.$emit("titleChanged","子向父组件传值");//自定义事件  传递值“子向父组件传值”

父组件通过props向下传递数据给子组件。(Vue的单向数据流)

注:组件中的数据共有三种形式:data、props、computed

二、父子组件-通过获取组件实例的方式— parent / $children 和 ref(ref更常用)

(标题写法:parent 前面也有$。编辑器的问题。下面也都有。)

$root :当前组件树的根 Vue 实例。如果当前实例没有父实例,此实例将会是其自己。

$parent:父实例,如果当前实例有的话。

$children

当前实例的直接子组件。需要注意 children并不保证顺序,也不是响应式的。如果你发现自己正在尝试使用children 并不保证顺序,也不是响应式的。如果你发现自己正在尝试使用 children 来进行数据绑定,考虑使用一个数组配合 v-for 来生成子组件,并且使用 Array 作为真正的来源。

子实例可以用 this.parent访问父实例,子实例被推入父实例的parent 访问父实例,子实例被推入父实例的 children 数组中。

需要注意的是:这两种都是直接得到组件实例,使用后可以直接调用组件的方法或访问数据。

this.$parent.message     //在子组件中访问父组件中的数据
this.$children[0].num    //因为父组件可能有很多子组件

注意:

节制地使用 $parent$children - 它们的主要目的是作为访问组件的应急方法。更推荐用 props 和 events 实现父子组件通信

ref: 如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素;如果用在子组件上,引用就指向组件实例。

注意:ref 也是很重要的一种方式,且实际工作中很常用。 除了可以使用它获取数据,还可以通过ref调用子组件中的方法,使其执行。

三、EventBus中央事件总线-emit/ on

这种方法通过一个空的Vue实例作为中央事件总线(事件中心),用它来触发事件和监听事件,巧妙而轻量地实现了任何组件间的通信,包括父子、兄弟、跨级。当我们的项目比较大时,可以选择更好的状态管理解决方案vuex。

var bus = new Vue()
// 组件A
bus.$emit('id-selected', 1)
// 组件B
bus.$on('id-selected', function (id) {
 console.log(id)
})

$on 监听了自定义事件,因为有时不确定何时会触发事件,一般会在 mounted 或 created 钩子中来监听。

四、Vuex

详见本人文章: Vuex的使用和原理 (待发布)

五、localstorage /sessionstorage使用

这种通信比较简单,缺点是数据和状态比较混乱,不太容易维护。

localStorage / sessionStorage可以结合vuex, 实现数据的持久保存,同时使用vuex解决数据和状态混乱问题.

六、attrs/listeners (前面都有$符)

多级组件嵌套需要传递数据时,通常使用的方法是通过vuex。但如果仅仅是传递数据,而不做中间处理,也可使用Vue2.4 版本提供的另一种方法----attrs/attrs/listeners。

vm.$attrs

包含了父作用域中不作为 prop 被识别 (且获取) 的 attribute 绑定 (class 和 style 除外)。当一个组件没有声明任何 prop 时,这里会包含所有父作用域的绑定 (class 和 style 除外),并且可以通过 v-bind="$attrs" 传入内部组件——在创建高级别的组件时非常有用。

$listeners:

包含了父作用域中的 (不含 .native 修饰器的) v-on 事件监听器。它可以通过 v-on="$listeners" 传入内部组件。

// 父组件 index.vue
<list class="list-box" title="标题" desc="描述" :list="list"></list>

// 子组件 list.vue
props: {
    list: [],
},
mounted() {
    console.log(this.$attrs)  // {title: "标题", desc: "描述"}
// 子组件 list.vue
<detail v-bind="$attrs"></detial>

// 孙子组件 detail.vue
// 不定义props,直接打印 $attrs
mounted() {
    console.log(this.$attrs)  // {title: "标题", desc: "描述"}

同样的,通过 listeners用类似的操作方式可以进行跨级的事件传递,实现子到父的通信。listeners 用类似的操作方式可以进行跨级的事件传递,实现子到父的通信。listeners 包含了父作用域中不含 .native 修饰的 v-on 事件监听器,通过 v-on="$listeners" 传递到子组件内部。

// 父组件 index.vue
<list @change="change" @update.native="update"></list>

// 子组件 list.vue
<detail v-on="$listeners"></detail>
// 孙子组件 detail.vue
mounted() {
    this.$listeners.change()
    this.$listeners.update() // TypeError: this.$listeners.update is not a function
}

七、provide / inject API (一般开发不用)

在根组件中,传入变量,然后在后代组件中使用即可。

image.png

// 父级组件提供 'foo'
var Provider = {
  provide: {
    foo: 'bar'
  },
  // ...
}
// 子组件注入 'foo'
var Child = {
  inject: ['foo'],
  created () {
    console.log(this.foo) // => "bar"
  }
  // ...
}

利用 ES2015 Symbols、函数 provide 和对象 inject:

const s = Symbol()

const Provider = {
  provide () {
    return {
      [s]: 'foo'
    }
  }
}

const Child = {
  inject: { s },
  // ...
}

慎用 provide/inject

provide 和 inject 主要在开发高阶插件/组件库时使用。并不推荐用于普通应用程序代码中。

Vuex 和 provide/inject 最大的区别在于,Vuex 中的全局状态的每次修改是可以追踪回溯的,而 provide/inject 中变量的修改是无法控制的,换句话说,你不知道是哪个组件修改了这个全局状态。

了解参考