使用事件总线在 Vue 组件之间共享 Props

Avatar of Kingsley Silas
Kingsley Silas

DigitalOcean 为您旅程的每个阶段提供云产品。立即开始使用 200 美元的免费额度!

默认情况下,Vue 组件之间的通信是通过使用 props 来实现的。Props 是从父组件传递到子组件的属性。例如,以下是一个组件,其中 title 是一个 prop

<blog-post title="My journey with Vue"></blog-post>

Props 始终是从父组件传递到子组件。随着应用程序复杂度的增加,您会逐渐遇到所谓的 props 钻取 这里有一篇相关的文章 侧重于 React,但也完全适用)。**Props 钻取**指的是将 props 一层层向下传递到子组件的过程——正如您可能想象的那样,这通常是一个繁琐的过程。

因此,繁琐的 props 钻取可能是复杂应用程序中的一个潜在问题。另一个问题与不相关组件之间的通信有关。我们可以通过使用**事件总线**来解决所有这些问题。

什么是事件总线?好吧,从名称本身就可以概括出来。它是一种传输方式,可以让一个组件将 props 从一个组件传递到另一个组件,无论这些组件在树中的什么位置。

实践任务:构建计数器

让我们一起构建一些内容来演示事件总线的概念。一个可以添加或减去提交的值并统计总计的计数器是一个不错的起点。

查看 CodePen 上 Kingsley Silas Chijioke (@kinsomicrote) 编写的 Vuejs 事件总线计数器

要使用事件总线,我们首先需要像这样初始化它

import Vue from 'vue';
const eventBus = new Vue();

这将 Vue 的一个实例设置为 eventBus。您可以根据需要为它命名。如果您正在使用单文件组件,那么您应该在单独的文件中包含代码片段,因为无论如何您都需要导出分配给 eventBus 的 Vue 实例。

import Vue from 'vue';
export const eventBus = new Vue();

完成此操作后,我们就可以开始在我们的计数器组件中使用它了。

以下是我们想要做的事情

  • 我们想要一个初始值为 0 的计数器。
  • 我们想要一个接受数值的输入字段。
  • 我们想要两个按钮:一个在单击时将提交的数值添加到计数器,另一个在单击时从计数器中减去提交的数值。
  • 我们想要确认计数器发生变化时的结果。

以下是包含所有这些元素的模板

<div id="app">
  <h2>Counter</h2>
  <h2>{{ count }}</h2>
  <input type="number" v-model="entry" />
  <div class="div__buttons">
    <button class="incrementButton" @click.prevent="handleIncrement">
      Increment
    </button>
    <button class="decrementButton" @click.prevent="handleDecrement">
      Decrement
    </button>
  </div>
  <p>{{ text }}</p>
</div>

我们将输入字段绑定到名为 entry 的值,我们将使用它来增加或减少计数,具体取决于用户输入的内容。当单击任一按钮时,我们将触发一个方法,该方法应增加或减少计数的值。最后,包含在 <p> 标签中的 {{ text }} 东西是我们将在其中打印总结计数器变化的消息。

以下是它们在我们脚本中的组合方式

new Vue({
  el: '#app',
  data() {
    return {
      count: 0,
      text: '',
      entry: 0
    }
  },
  created() {
    eventBus.$on('count-incremented', () => {
      this.text = `Count was increased`
      setTimeout(() => {
        this.text = '';
      }, 3000);
    })
    eventBus.$on('count-decremented', () => {
      this.text = `Count was decreased`
      setTimeout(() => {
        this.text = '';
      }, 3000);
    })
  },
  methods: {
    handleIncrement() {
      this.count += parseInt(this.entry, 10);
      eventBus.$emit('count-incremented')
      this.entry = 0;
    },
    handleDecrement() {
      this.count -= parseInt(this.entry, 10);
      eventBus.$emit('count-decremented')
      this.entry = 0;
    }
  }
})

您可能已经注意到,我们即将通过查看代码来使用事件总线。

首先,我们必须为从一个组件向另一个组件发送事件建立一条路径。我们可以使用 eventBus.$emit()(其中 emit 是发送的时髦说法)来铺设这条路径。该发送包含在两个方法 handleIncrementhandleDecrement 中,这两个方法正在侦听输入提交。并且,一旦发生,我们的事件总线就会向任何请求数据的组件发送 props。

您可能已经注意到,我们正在使用 eventBus.$on()created() 生命周期钩子中侦听这两个事件。在两个事件中,我们都必须传入与我们发出的事件相对应的字符串。这就像特定事件的标识符以及为组件接收数据建立方式的标识符。当 eventBus 识别到已宣布的特定事件时,将调用后面的函数——我们将设置一个文本以显示发生了什么,并在三秒后使其消失。

实践任务:处理多个组件

假设我们正在开发一个个人资料页面,用户可以在其中更新其应用程序的姓名和电子邮件地址,然后在不刷新页面的情况下查看更新。即使我们这次处理两个组件:用户个人资料和提交个人资料更改的表单,也可以使用事件总线流畅地实现此功能。

查看 CodePen 上 Kingsley Silas Chijioke (@kinsomicrote) 编写的 Vuejs 事件总线 2

这是模板

<div class="container">
  <div id="profile">
    <h2>Profile</h2>
    <div>
      <p>Name: {{name}}</p>
      <p>Email: {{ email }}</p>
    </div>
  </div>

  <div id="edit__profile">
    <h2>Enter your details below:</h2>
    <form @submit.prevent="handleSubmit">
      <div class="form-field">
        <label>Name:</label>
        <input type="text" v-model="user.name" />
      </div>
      <div class="form-field">
        <label>Email:</label>
        <input type="text" v-model="user.email" />
      </div>
      <button>Submit</button>
    </form>
  </div>
</div>

我们将 idsuser.nameuser.email)传递到相应的组件。首先,让我们设置“编辑个人资料”(edit__profile)组件的模板,该组件包含我们想要传递到接下来将设置的“个人资料”组件的姓名和电子邮件数据。同样,我们在检测到提交事件发生后,建立了一个事件总线来发出该数据。

new Vue({
  el: "#edit__profile",
  data() {
    return {
      user: {
        name: '',
        email: ''
      }
    }
  },
  methods: {
    handleSubmit() {
      eventHub.$emit('form-submitted', this.user)
      this.user = {}
    }
  }
})

此数据将用于在“个人资料”(profile)组件中以响应方式更新用户个人资料,该组件正在查找当总线到达其集线器时传入的 nameemail

new Vue({
  el: '#profile',
  data() {
    return {
      name: '',
      email: ''
    }
  },
  created() {
    eventHub.$on('form-submitted', ({ name, email}) => {
      this.name = name;
      this.email = email
    })
  }
})

他们的行李已经打包。现在他们需要做的就是回家了。

很酷,对吧?即使“编辑个人资料”和“个人资料”组件不相关(或不存在直接的父子关系),它们也可以相互通信,通过相同的事件连接。

一路畅通

我发现事件总线在我想要在应用程序中启用响应性时很有帮助——特别是,在不导致页面刷新的情况下,根据从服务器获得的响应更新组件。发出的事件也可能被多个组件监听。

如果您还有其他使用事件总线的有趣场景,我很乐意在评论中听到它们。🚌