Vue3 组件化开发(5)——组件间的通信(1)

638 阅读4分钟

「这是我参与2022首次更文挑战的第25天,活动详情查看:2022首次更文挑战」。

假设 App 组件可拆分为如下结构:

组件的拆分

那么各组件的嵌套逻辑(存在的关系)如下:

  • App 组件是 HeaderMainFooter 组件的父组件HeaderMainFooter 组件是 App 组件的子组件
  • Main 组件是 BannerProductList 组件的父组件BannerProductList 组件是 Main 组件的子组件
  • HeaderMain非父子组件HeaderFooter 是非父子组件,HeaderBanner 也是非父子组件,等等;

在开发过程中,我们会经常遇到需要组件之间(最常见的是父子组件之间)相互进行通信的情形:

  • 比如 App 可能使用了多个 Header,每个地方的 Header 展示的内容不同,那么我们就需要使用者传递给 Header 一些数据,让其进行展示;
  • 又比如我们在 Main 中一次性请求了 Banner 数据和 ProductList 数据,而这些数据不是在 Main 中展示的,而是由 BannerProductList 进行展示,那么就需要把数据传递给 BannerProductList 组件来进行展示;
  • 也可能是子组件中发生了事件,需要由父组件来完成某些操作,那就需要子组件向父组件传递事件

总之,在一个 Vue 项目中,组件之间的通信是非常重要的环节,所以下面我们就具体学习一下组件之间是如何相互传递数据的。

1. 父子组件之间通信的方式

父子组件之间如何进行通信呢?

  • 父组件传递数据给子组件:通过 props 属性;
  • 子组件传递给父组件:通过 $emit 触发事件;

父子组件之间的通信

2. 父组件传递给子组件

在开发中,最常见的可能就是父子组件之间的通信了,比如父组件有一些数据,需要子组件来进行展示时,我们就可以通过 props 来完成父组件向子组件的通信

什么是 props 呢?

  • props 是你可以在组件上注册的一些自定义的 attribute(属性)

    原生的 HTML 元素上会有一些属性(attribute),如:classidstyle 等等;而对于 Vue 组件来说,也可以有一些自己的属性(attribute),我们可以在组件中通过 props 选项来注册一些属于组件自己属性(attribute)。

  • 先在子组件中使用 props 选项注册好自己的 prop 属性(attribute)列表,之后父组件就可以传递数据给子组件的某个 prop 属性(attribute子组件就能通过这个 prop 属性(attribute)的名称获取到父组件传递过来的对应数据

props 的注册有两种方式:

  1. 字符串数组
    • 数组中的字符串就是 attribute 的名称;
  2. 对象
    • 通过使用对象类型,我们可以在指定 attribute 名称的同时,指定该 attribute 接受的数据类型、是否是必须的、默认值等等;

假如有这样一个需求:父组件传递一些数据给子组件,到时候在子组件中展示这些数据。

我们在项目目录(learn_component)下的 src 目录下新建 03_父组件传递子组件 文件夹,在该文件夹中新建 App.vue(作为父组件)和 ShowMessage.vue(作为子组件)文件。然后,按以下步骤实现上面的需求:

  1. 在子组件(这里即 ShowMessage.vue)中的 props 选项中按需求注册 prop(这里以字符串数组的方式举例):

    <template>
      <div>
    
      </div>
    </template>
    
    <script>
      export default {
        props: ['title', 'content']
      }
    </script>
    
    <style scoped>
    
    </style>
    
  2. 在父组件(这里即 App.vue)中导入并使用子组件(ShowMessage.vue),并通过上一步在子组件中注册的两个属性(titlecontent)传递数据:

    <template>
      <div>
        <show-message title="哈哈哈" content="我是哈哈哈"></show-message>
      </div>
    </template>
    
    <script>
      import ShowMessage from './ShowMessage.vue'
    
      export default {
        components: {
          ShowMessage
        }
      }
    </script>
    
    <style scoped>
    
    </style>
    
  3. 子组件接收到 titlecontent 这两个 attribute 传递过来的数据后,就可以在自己的模板中访问 titlecontent 这两个 property 的值了:

    <template>
      <div>
        <h2>{{ title }}</h2>
        <p>{{ content }}</p>
      </div>
    </template>
    
    <script>
      export default {
        props: ['title', 'content']
      }
    </script>
    
    <style scoped>
    
    </style>
    

为了看到对应的效果,我们需要修改一下 src/main.js 中引入 App.vue 组件的路径:

import { createApp } from 'vue'
import App from './03_父组件传递子组件/App.vue'

createApp(App).mount('#app')

页面效果如下:

image-20220102211742077

可见,成功实现了前面的需求(父组件传递一些数据给子组件,到时候在子组件中展示这些数据)。

也就是说,我们已经实现了父组件传递给子组件什么数据,子组件就能展示什么数据的功能。

并且父组件中也可以使用多个子组件,传递多份数据:

<template>
  <div>
    <show-message title="哈哈哈" content="我是哈哈哈"></show-message>
    <show-message title="呵呵呵" content="我是呵呵呵"></show-message>
    <show-message title="嘻嘻嘻" content="我是嘻嘻嘻"></show-message>
  </div>
</template>

<script>
  import ShowMessage from './ShowMessage.vue'

  export default {
    components: {
      ShowMessage
    }
  }
</script>

<style scoped>

</style>

效果如下:

image-20220102213054668

当然,上面我们是直接把数据传给了属性,我们也可以先把数据保存到变量中,再通过变量把数据传给属性,那么,我们就可以在 data 选项中定义变量之后通过 v-bind 将变量绑定到对应属性上:

<template>
  <div>
    <show-message title="哈哈哈" content="我是哈哈哈"></show-message>
    <!-- 绑定 data 选项中的数据 -->
    <show-message :title="title" :content="content"></show-message>
    <show-message :title="message.title" :content="message.content"></show-message>
    <!-- 绑定 data 选项中的一个对象数据 -->
    <show-message v-bind="message"></show-message>
  </div>
</template>

<script>
  import ShowMessage from './ShowMessage.vue'

  export default {
    components: {
      ShowMessage
    },
    data() {
      return {
        title: '呵呵呵',
        content: '我是呵呵呵',
        message: {
          title: '嘻嘻嘻',
          content: '我是嘻嘻嘻'
        }
      }
    }
  }
</script>

<style scoped>

</style>

如果想要传递整个对象中的属性和值,还可以直接 v-bind 这个对象:

<template>
  <div>
    <show-message title="哈哈哈" content="我是哈哈哈"></show-message>
    <show-message :title="title" :content="content"></show-message>
    <show-message :title="message.title" :content="message.content"></show-message>
    <show-message v-bind="message"></show-message>
  </div>
</template>

<script>
  import ShowMessage from './ShowMessage.vue'

  export default {
    components: {
      ShowMessage
    },
    data() {
      return {
        title: '呵呵呵',
        content: '我是呵呵呵',
        message: {
          title: '嘻嘻嘻',
          content: '我是嘻嘻嘻'
        }
      }
    }
  }
</script>

<style scoped>

</style>

2.1 父传子 - props 数组用法

子组件中的 props 选项可以对应一个字符串数组:

image-20220102215316690