在组合各类vue组件的时候,为了保证数据传递的一致性,我们需要这些通信机制。

一、props/$emit/$on

使用场景

通常用于“父子”通信。

时序图

Vue的通信机制 - 图1

优点

  • 可追溯:父组件调用时,会声明有哪些“属性”传给孩子;会声明有哪些“方法”可以让孩子上报父级。

如果你编写正确的话,可以让代码“清晰明了”,方便追溯两边的传递。

缺点

  • 只能传递一级,当需要“祖师爷 -> 爷爷 -> 父亲 -> 孩子”,使用起来不太方便。

    示例

    这是我们最常用的属性,也最为清楚;

  • 子组件通过 props 属性【接收】父组件传来的属性

    1. props: {
    2. // 优惠券、优惠码
    3. detailType: {
    4. type: String,
    5. default: 'ticket',
    6. },
    7. // 查看、复制、编辑、新建
    8. enterType: {
    9. type: String,
    10. default: 'add',
    11. },
    12. // 复制、编辑、查看的依据
    13. pkId: {
    14. type: [String, Number],
    15. default: '',
    16. },
    17. },
  • 子组件通过 $emit 属性【发送】改变的信息给父组件

    1. this.$emit('update-callback')
  • 父组件也存在关于子组件的表达

    1. <ForbidReceiveDialog ref="forbidReceiveDom"
    2. v-if="forbidReceiveVisible"
    3. :enterType="'just test'"
    4. @update-callback="reload">
    5. </ForbidReceiveDialog>

    可以看到,父组件的引用,显示地体现了子组件有哪些:【被传递的值】【可像父级上报的函数】

二、$attrs/$listeners

有时候我们想从 爷爷传递给孙子,如果通过 props 来传递的话,可能就需要多余的传递。

使用场景

【跨级传递】爷爷组件,想要传值给孙子组件,父亲节点用不上这些属性。

因为父亲节点用不上,多写一次没必要

时序图

Vue的通信机制 - 图2

优点

  • 让代码更简洁。(如果通过 props 的方式实现,需要多传一些实际上用不到的值)

    缺点

  • 不容易追溯。(除Vuex的解决方案,由于传递层级过深,都有这个问题)

    示例

    爷爷组件A
    1. <attrA @testL="testL" :a="a" :b="b" :c="c" :d="d"></attrA>

    父亲组件B
    1. <attrAB v-bind="$attrs" v-on="$listeners"></attrAB>

    孙子组件C
    1. {{$attrs.a || '该属性没有'}}
    2. {{$attrs.b || '该属性没有'}}
    3. {{$attrs.c || '该属性没有'}}
    4. {{$attrs.d || '该属性没有'}}
    5. <button @click="triggerListeners">触发$listeners</button>
    1. triggerListeners() {
    2. this.$emit('testL')
    3. }

    三、provide/inject

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

  • 需要成对使用

    使用场景

    需要传递给“所有子孙后代”的【“方法”、“属性”】

    时序图

    Vue的通信机制 - 图3

    优点

  • 可追溯。通过 inject 来声明,不会覆盖子级本身的变量。

  • 特定的应用场景。能够传给所有子孙级,快速方便。

    缺点

  • 不容易追溯。(除Vuex的解决方案,由于传递层级过深,都有这个问题)

    示例一、普通的传递

    父级——provide提供【“属性”、“方法”】

    1. provide: {
    2. global: 'I\'m global.',
    3. provideKey: 'balbalaj',
    4. fn: () => {
    5. console.log('hhh')
    6. }
    7. },

    子孙级——inject进来的【“属性”、“方法”】

    1. {{selfName}}
    2. <button @click="fn">provide提供函数,inject进来</button>
    1. inject: {
    2. selfName: {
    3. from: 'global',
    4. default: 'xxXx'
    5. },
    6. fn: 'fn'
    7. },

    示例二、用 provide/inject 实现页面的reload

    父级——template

    1. <main-content v-if="!$store.state.common.contentIsNeedRefresh" />

    父级——provide提供

    1. provide () {
    2. return {
    3. // 刷新
    4. refresh () {
    5. this.$store.commit('common/updateContentIsNeedRefresh', true)
    6. this.$nextTick(() => {
    7. this.$store.commit('common/updateContentIsNeedRefresh', false)
    8. })
    9. }
    10. }
    11. },

    子级——inject引入

    1. inject: ['refresh'],

    四、$refs/$parent/$children

    使用场景

    $refs有其单独的使用场景,并不针对于通信传值。

事实上这种并不太推荐使用。

时序图

Vue的通信机制 - 图4

优点

  • 快捷。

    缺点

  • 不好追溯。

    事实上不应有太多这样的操作。

示例

  1. <attrA ref="targetDom"></attrA>
  2. <button @click="getRef">获取refs</button>
  3. <button @click="getChildren">获取children</button>
  4. <button @click="getParent">获取parent</button>
    getRef() {
      console.log(this.$refs.targetDom)
    },
    getChildren() {
      console.log(this.$children)
    },
    getFather() {
      console.log(this.$parent)
    }

五、eventBus

使用场景

事件总线机制,思想为Pub/Sub模式的实现。
让所有的事件平行的传递,用于【较小项目】中各级组件的传递。

时序图

Vue的通信机制 - 图5

优点

  • 简洁。

    缺点

  • 项目大,不易维护。

    示例

    初始化总线 ``` import Vue from ‘vue’

export default new Vue()

兄弟节点A引入总线

import EventBus from ‘@/eventBus.js’

兄弟节点A监听事件

mounted() { EventBus.$on(‘changeFromA’, () => { console.log(‘changeFromA’) }) },

兄弟节点B引入总线

import EventBus from ‘@/eventBus.js’

兄弟节点B触发事件
changeMethod() {
  EventBus.$emit('changeFromA', {
    a: 99,
    b: 1,
    c: 1
  })
},
<a name="umFoi"></a>
### 六、Vuex
<a name="nUSKT"></a>
#### 使用场景
较大的项目。多处需要共享状态。
> Vuex是单向数据流。

<a name="ZJdHg"></a>
#### 时序图
![](https://cdn.nlark.com/yuque/__puml/b07be481d4fcce351eea382971a09fef.svg#lake_card_v2=eyJjb2RlIjoiQWN0aW9uIC0-IE11dGF0aW9uOkNvbW1pdFxuTXV0YXRpb24gLT4gU3RhdGU6TXV0YXRlXG5TdGF0ZSAtPiBWaWV3OlJlbmRlclxuVmlldyAtPiBBY3Rpb246RGlzcGF0Y2giLCJ0eXBlIjoicHVtbCIsIm1hcmdpbiI6dHJ1ZSwiaWQiOiJsOG05TyIsInVybCI6Imh0dHBzOi8vY2RuLm5sYXJrLmNvbS95dXF1ZS9fX3B1bWwvYjA3YmU0ODFkNGZjY2UzNTFlZWEzODI5NzFhMDlmZWYuc3ZnIiwiY2FyZCI6ImRpYWdyYW0ifQ==)<a name="9C4TG"></a>
#### 优点

- 易追溯。
<a name="9191x"></a>
#### 缺点

- 较大。
<a name="1ZF2R"></a>
#### 示例

this.$store.dispatch(‘actionFn’, data) this.$store.commit(‘mutateFn’, data)

mutateFn (state, obj) { state.obj = obj } get () { return this.$store.state.common.menuActiveName } ```