class 9: vue.js 3 组件化基础(2)父子组件间通信

父子组件之间的相互通信

  • 开发过程中,我们通常会将一个页面拆分成多个组件,然后将这些组件通过组合或者嵌套的方式构建页面。组件的嵌套由父组件和子组件组成,它们之间的通信如下图所示
    在这里插入图片描述
  • 父组件传递数据给子组件是通过props属性实现的;而子组件传递数据给父组件,是通过触发事件$emit实现的

父组件传递数据给子组件

Prop为字符串类型的数组

  • Props是在组件上注册自定义属性的一种方式。当父组件为自定义属性赋值后,子组件可以通过属性名获取对应的值。Props一般可用来传递字符串类型的数组或者对象类型
<!--ShowMessage.vue-->
<script>
export default {
  props: ['title', 'content']
}
</script>

<template>
  <div class="show-message">
    <h4>{{title}}</h4>
    <div>{{content}}</div>
  </div>
</template>

<style scoped>

</style>
<!--App.vue-->
<template>
  <div class="app">
<!--    1. 直接传递字符串-->
    <ShowMessage title="我是标题" content="我是内容">
    </ShowMessage>
<!--    2. 绑定字符串类型的变量-->
    <ShowMessage :title="title" :content="content">
    </ShowMessage>
<!--    3. 绑定对象中字符串类型的属性-->
    <ShowMessage :title="message.title" :content="message.content">
    </ShowMessage>
<!--    4. 直接绑定一个对象,会自动将对象的每个属性拆出来逐一绑定-->
    <show-message v-bind="message">
    </show-message>
  </div>
</template>
<script>
import ShowMessage from "./ShowMessage.vue";
export default {
  components: {
    ShowMessage,
  },
  data() {
    return {
      title: "我是标题title",
      content: '我是内容content',
      message: {
        title: '我是标题message.title',
        content: '我是内容message.content'
      }
    }
  }
}
</script>
  • 上面实现了从父组件app传递数据到子组件ShowMessage

Prop为对象类型

<!--ShowMessage.vue-->
<script>
export default {
  // 1. props是数组
  // props: ['title', 'content']
  // 2. props是对象
  props: {
    title: String, // 定义title属性为String类型 (这里是简写, 下面content属性是完整的写法)
    content: {
      type: String, // 定义参数类型为String类型
      required: true, // 父组件使用该组件时必须传递该参数,否则控制台会出现警告
      default: '我是内容的默认值' // 如果父组件使用该组件时没有传递content参数,则使用默认值
    }
  }
}
</script>

<template>
  <div class="show-message">
    <h4>{{title}}</h4>
    <div>{{content}}</div>
  </div>
</template>

<style scoped>

</style>
  • Props支持camelCase(驼峰)和kebab-case(连字符分隔)这两种方式,在HTML中,属性名不分大小写,浏览器会将所有大写字符解释为小写字符。因此,在模板中使用camelCase命名法的Props时,也可以使用其等效的kebab-case语法。
  • 例如我们为ShowMessage.vue子组件添加一个messageInfo属性。该属性支持camelCasekebab-case两种方式,如下,这两种方式是等价的
<ShowMessage message-info="我是message-info字符串" content="">
</ShowMessage>
<show-message messageInfo="我是messageInfo字符串" content="">
</show-message>
  • 我们只需要在ShowMessage.vue文件中把props改成下面这样即可
<script>
export default {
  // 1. props是数组
  // props: ['title', 'content']
  // 2. props是对象
  props: {
    title: String, // 定义title属性为String类型 (这里是简写, 下面content属性是完整的写法)
    content: {
      type: String, // 定义参数类型为String类型
      required: true, // 父组件使用该组件时必须传递该参数,否则控制台会出现警告
      default: '我是内容的默认值' // 如果父组件使用该组件时没有传递content参数,则使用默认值
    },
    messageInfo: {
      type: String,
    }
  }
}
</script>
  • 这样就能在组件ShowMessage中接收到父组件传进来的messageInfo属性了
  • 除了Props属性,我们还经常会为组件传递id,class等属性,这些属性被称为非Props的属性。当我们为一个组件传递某个属性,但是该属性并没有定义对应的propsemits时,就称之为非Props的属性,常见的有class,style,id等。当组件只有单个根节点时,这些非PropsAttribute将被自动添加到根节点的属性中,这被称为属性继承。如下所示,虽然在NoPropAttribute组件中并没有定义Props,但是id,class,name这三个属性还是被自动添加到了根节点的属性中
<!--NoPropAttribute.vue-->
<script setup>

</script>

<template>
  <div class="no-prop-attribute">
    该子组件没有定义任何的props属性
  </div>
</template>

<style scoped>

</style>
<!--App.vue-->
<script setup>

import NoPropAttribute from "@/chapters/chapter05/NoPropAttribute.vue";
</script>

<template>
  <div class="app">
    <no-prop-attribute id="coder" class="why" name="coderwhy">

    </no-prop-attribute>
  </div>
</template>

<style scoped>

</style>

在这里插入图片描述

  • 那么我们有时候有这样的场景,我们不希望组件的根元素继承属性。那么该如何禁用非Props的属性继承呢?在组件中设置inheritAttr: false即可。可以通过$attrs访问所有的非Props的属性,并应用于根元素之外的其他元素。如下所示
<!--NoPropAttribute.vue-->
<script>
export default {
  inheritAttrs: false,
}
</script>

<template>
  <div class="no-prop-attribute">
    该子组件没有定义任何的props属性
  </div>
</template>

<style scoped>

</style>

在这里插入图片描述

  • 如果想获取非Props的属性,需要使用$attr获取,如下
<!--NoPropAttribute.vue-->
<script>
export default {
  inheritAttrs: false,
}
</script>

<template>
  <div class="no-prop-attribute" :id="$attrs.id">
    该子组件没有定义任何的props属性
  </div>
</template>

<style scoped>

</style>

子组件传递数据给父组件

  • 子组件传递数据给父组件的需求也是非常普遍的,例如,子组件发生点击事件,需要传递一些索引等信息给父组件,父组件再进行刷新数据等操作, 下面是一个例子
<!--App.vue-->
<script>
import CounterOperation from "@/chapters/chapter06/CounterOperation.vue";
export default {
  components: {
    CounterOperation
  },
  data() {
    return {
      counter: 0
    }
  },
  methods: {
    addOne() {
      this.counter++
    },
    subOne() {
      this.counter--
    }
  }
}
</script>

<template>
  <div>
    <h4>当前计数: {{counter}}</h4>
    <counter-operation @add="addOne" @sub="subOne">

    </counter-operation>
  </div>
</template>

<style scoped>

</style>
<!--CounterOperation.vue-->
<script>
export default {
  emits: ["add", "sub"],
  methods: {
    increment() {
      this.$emit("add")
    },
    decrement() {
      this.$emit("sub")
    }
  }
}
</script>

<template>
  <div>
    <button @click="increment">
      +1
    </button>
    <button @click="decrement">
      -1
    </button>
  </div>
</template>

<style scoped>

</style>
  • 在子组件中定义触发事件的名称emits: ["add", "sub"],父组件中以v-on的形式传入要监听的事件名称,并绑定到对应的方法中,例如@add="addOne",然后子组件中发生事件的时候,根据事件名称,使用$emit函数触发对应的事件,例如this.$emit("add")
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Clarence Liu

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值