如下图所示, 子组件 和 子组件 间可以通过 共享一个父组件 或全局事件总线 / 状态管理库 实现数据通信

堂兄弟 🎈 组件间:建议采用 共享一个父组件的方案

表兄弟组件间:建议采用 全局事件总线 / 状态管理库的方案

兄弟关系图

  如下图所示,D 组件和 E 组件是堂兄弟,而 F 组件与 D/E 组件是表兄弟。

# 一、全局事件总线

  Vue.js 框架提供了一个全局事件总线(Global Event Bus),也称为 Vue 实例事件系统。

该事件总线允许在应用程序中的任何组件之间进行通信,无论它们是否具有父子关系

注意

  • 在 Vue.js 2.x 及更早版本中,EventBus 是一个基于 Vue 实例的解决方案
  • 在 Vue.js 3.x 及以上版本中,全局事件 API 已经被重构,EventBus 被视为一种过时的模式,应使用新的全局事件钩子函数来代替(或者使用官方推荐的 mitt (opens new window)、tiny-emitte 库)
原因

  其实我们细细想来,vue3 去除 EventBus(去除了$on$off$once等)也是非常合理的。

  在 vue 中,数据共享的方案先如今已经非常多了:

  • 父子间:props、$emit等(vue3 中子传父不能使用$on$off方式也没关系,反正基本不用)
  • 兄弟间:共享父组件、状态管理库(由于 pinia 的盛行,vue3 中的 EventBus 显得有点多余)
  • 其他:
    • 上下游(provide、inject)
    • 边界处理($refs 等)

# 1、EventBus

  EventBus 表示事件总线,它实际上是一个 Vue 实例,被用作一个中央事件处理器,所有其他组件都可以通过订阅和发布事件来与它通信。

  • 方式 1

直接 new 一个崭新的 vue 实例对象作为 EventBus

// eventBus.js
import Vue from 'vue'

// 向外共享Vue的实例对象
export default new Vue()
  • 方式 2 👍

在 vm 的原型上挂载一个$bus 作为 EventBus

// main.js
import Vue from 'vue'
import App from './App.vue'

Vue.config.productionTip = false

new Vue({
  render: (h) => h(App),
  beforeCreate() {
    // 这样vue原型上的$bus就有了和vm/vc一样的方法和属性了
    Vue.prototype.$bus = this
  }
}).$mount('#app')

# 2、数据共享

  我们在自定义事件部分已经了解到了:

  • $on:监听当前父组件为子组件定义的的自定义事件/事件回调
  • $emit:触发父组件为当前子组件绑定的自定义事件/事件回调

  在 vue 中我们可以基于上面两个知识点实现结合 EventBus,就可以实现各个组件间的数据通信了。

下面我们以 HomeView 组件向 AboutWorld 组件表白为例:

  • AboutWorld.vue
<template>
  <div class="home">
    <h3>AboutWorld组件内容</h3>
  </div>
</template>

<script>
export default {
  name: 'AboutWorld',
  mounted() {
    this.$bus.$on('homeToAbout', (val) => {
      console.log('我监听到你触发了$emit,我收到的数据为:\n', val)
    })
  },
  // 注意事项
  beforeDestroy() {
    this.$bus.$off('homeToAbout')
  }
}
</script>
  • Send.vue
<template>
  <div class="home">
    <h3>HomeView组件内容</h3>

    <button @click="sendMsg">向AboutWorld表白</button>
    <br /><br />

    子组件内容如下:
    <hello-world></hello-world>
  </div>
</template>

<script>
import HelloWorld from '../components/HelloWorld'

export default {
  name: 'HomeView',
  components: {
    HelloWorld
  },
  data() {
    return {
      interact: '你可以教我包饺子吗?我有点笨,做什么都容易露馅,喜欢你也是~'
    }
  },
  methods: {
    sendMsg() {
      this.$bus.$emit('homeToAbout', this.interact)
    }
  }
}
</script>

# 二、vue 的数据流

# 1、依赖注入 ✨

  在父组件只要声明了provide,在其子组件,孙组件,曾孙组件等能形成上下游关系的组件中交互。

无论多深都能通过inject来访问provide中的数据。

提示

  这个一般使用较少,如果在 vue 中使用依赖注入,表明当前项目存在大量的组件嵌套,是不利于维护的。

但在 react 中由于高阶组件的存在,使用依赖注入的概率会高于 vue。

<script>
export default {
  // provide 提供给后代✨组件的数据/方法
  // provide: {
  //   message: 'provided by father'
  // }
  provide() {
    return {
      message: 'provided by father'
    }
  }
}
</script>
<script>
export default {
  // 使用 inject 选项来接收由provide提供的、当前组件✨需要的数据/方法
  inject: ['message']
}
</script>
使用示例
  • provide
<template>
  <div>
    <p>{{ title }}</p>
    <son></son>
  </div>
</template>

<script>
import Son from "./son"

export default {
  name: 'Father',
  components: { Son },

  // provide 提供给后代✨组件的数据/方法
  provide: {
    message: 'provided by father'
  },
  data () {
    return {
      title: '父组件'
    }
  },
  methods: { ... }
}
</script>
  • 过渡:
<template>
  <div>
    <p>{{ title }}</p>
    <grand-son></grand-son>
  </div>
</template>
<script>
import grandSon from './grandSon'

export default {
  name: 'Son',
  components: { grandSon },
  data() {
    return {
      title: '子组件'
    }
  }
}
</script>
  • inject
<template>
  <div>
    <p>message:{{ message }}</p>
  </div>
</template>
<script>
export default {
  name: "GrandSon",

  // 使用 inject 选项来接收由provide提供的、当前组件✨需要的数据/方法
  inject: [ "message" ],
  data () {
    return {
      title: '孙组件'
    }
  },
  methods: { ... }
};
</script>
拓展:provide 与 响应式数据 🤔

  通过测试,我们发现修改 provide 中的数据时,inject 中使用的数据并不会响应式更新。

解决方案:

我们使用一些响应式 API 来完成功能:比如 computed 函数

<script>
export default {
  data() {
    return {
      dataToProvide: {
        count: 0
      }
    }
  },
  provide() {
    return {
      dataToProvide: Vue.observable(this.dataToProvide)
    }
  }
}
</script>

# 2、处理边界

  • $refs:访问子组件的实例

这个可以用于父组件触发子组件方法的场景中

  • $root :访问根实例 *
  • $parent:访问父组件的实例 *

# 3、状态管理库

vuex、pinia、redux

# 二、消息发布订阅

方式 1:

使用上面的 Vue 实例事件系统

方式 2:

使用 pubsub-js 包即可

更新于 : 8/7/2024, 2:16:31 PM