组件间通信有哪些方式?

1. 组件间通信的分类

组件间通信可以按以下维度分类:

  • 父子通信:props / $emit / ref / $parent / $children
  • 隔代通信:provide / inject / $attrs / $listeners
  • 兄弟通信:EventBus / Vuex
  • 全局通信:Vuex / EventBus

2. 整理 Vue 中 8 种常规的通信方案

(1) props 传递数据

props: {
  // 字符串形式
  name: String, // 接收的类型参数
  // 对象形式
  age: {
    type: Number,    // 接收的类型为数值
    default: 18,     // 默认值为 18
    required: true   // age 属性必须传递
  }
}
<Children name="jack" :age="18" />

(2) $emit 触发自定义事件

this.$emit('add', good)
<Children @add="cartAdd($event)" />

(3) ref

<Children ref="foo" />
this.$refs.foo // 获取子组件实例,通过子组件实例我们就能拿到对应的数据

(4) EventBus

// 创建一个中央事件总线类
class Bus {
  constructor() {
    this.callbacks = {} // 存放事件的名字
  }
  $on(name, fn) {
    this.callbacks[name] = this.callbacks[name] || []
    this.callbacks[name].push(fn)
  }
  $emit(name, args) {
    if (this.callbacks[name]) {
      this.callbacks[name].forEach(cb => cb(args))
        }
  }
}

// main.js
Vue.prototype.$bus = new Bus() // 将 $bus 挂载到 vue 实例的原型上
// 另一种方式:Vue 已经实现了 Bus 的功能
Vue.prototype.$bus = new Vue()
// Children1.vue
this.$bus.$emit('foo')

// Children2.vue
this.$bus.$on('foo', this.handle)

vm.$on(event, callback) 触发当前实例上的事件。附加参数都会传给监听器回调。多次给同一个事件绑定 callback 不会覆盖,而是会依次执行。

(5) $parent / $root(兄弟 / 根)

// 兄弟组件
this.$parent.on('add', this.add)

// 另一个兄弟组件
this.$parent.emit('add')

(6) $attrs 与 $listeners

<!-- 给 Grandson 隔代传值,communication/index.vue -->
<Child2 msg="lalala" @some-event="onSomeEvent" />

<!-- Child2 做展开 -->
<Grandson v-bind="$attrs" v-on="$listeners" />

<!-- Grandson 使用 -->
<div @click="$emit('some-event', 'msg from grandson')">
  {{ msg }}
</div>

注:Vue 3 中 $listeners 已被移除,事件监听器会作为 attrs 的一部分传给子组件。

(7) provide 与 inject

祖先组件通过 provide 提供数据,后代组件通过 inject 注入数据。

// 祖先
export default {
  provide() {
    return { foo: 'bar' }
  }
}

// 后代
export default {
  inject: ['foo'],
  mounted() {
    console.log(this.foo) // 'bar'
  }
}

(8) Vuex

actions: {
  actionA(context) {
    context.commit('mutationMethod')
  },
  actionB({ commit, state }) {
    commit('mutationMethod2')
  }
}

来源整理自:vue3js.cn 面试官系列