组件的通信

  • 上面的嵌套逻辑如下,它们存在如下关系:
    • App组件是Header、Main、Footer组件的父组件;
    • Main组件是Banner、ProductList组件的父组件;
  • 在开发过程中,我们会经常遇到需要组件之间相互进行通信:
    • 比如App可能使用了多个Header,每个地方的Header展示的内容不同,那么我们就需要使用者传递给Header一些数据,让其进行展示;
    • 又比如我们在Main中一次性请求了Banner数据和ProductList数据,那么就需要传递给它们来进行展示;
    • 也可能是子组件中发生了事件,需要由父组件来完成某些操作,那就需要子组件向父组件传递事件;
  • 总之,在Vue项目中,组件之间的通信是非常重要的环节

父子组件之间通信的方式

  • 父子组件之间如何进行通信呢?
    • 父组件传递给子组件:通过props属性;
    • 子组件传递给父组件:通过$emit触发事件;

image.png

父组件传递给子组件

父子组件之间,可以通过props来完成组件之间的通信;

  • 什么是Props呢?
    • Props是组件上注册的一些自定义attribute;
    • 父组件给这些attribute赋值,子组件通过attribute的名称获取到对应的值;
  • Props有两种常见的用法:
    • 方式一:字符串数组,数组中的字符串就是attribute的名称;
    • 方式二:对象类型,对象类型我们可以在指定attribute名称的同时,指定它需要传递的类型、是否是必须的、默认值等等;

Props的数组用法

image.png

Props的对象用法

  • 数组用法中只能说明传入的attribute的名称,并不能对其进行任何形式的限制。
  • 当使用对象语法的时候,可以对传入的内容限制更多:

    • 比如指定传入的attribute的类型;
    • 比如指定传入的attribute是否是必传的;
    • 比如指定没有传入时,attribute的默认值;
      1. props: {
      2. name: String,
      3. age: {
      4. type: Number,
      5. required: true,
      6. },
      7. content: {
      8. type: String,
      9. required: true,
      10. default: "我是默认值",
      11. },
      12. },

      type的类型都可以是哪些

  • String

  • Number
  • Boolean
  • Array
  • Object
  • Date
  • Function
  • Symbol

对象类型的其他写法

image.png
image.png

prop 的大小写命名(camelCase vs kebab-case)

  • HTML 中的 attribute 名是大小写不敏感的,所以浏览器会把所有大写字符解释为小写字符;
  • 这意味着当你使用 DOM 中的模板时,camelCase (驼峰命名法) 的 prop 名需要使用其等价的 kebab-case (短横线分隔命名) 命名;

image.png

非Prop的Attribute

  • 什么是非Prop的Attribute呢?
    • 传递给组件某个属性,但是该属性并没有定义对应的props或者emits时,就称之为 非Prop的Attribute;
    • 常见的包括class、style、id属性等;
  • Attribute继承
    • 当组件有单个根节点时,非Prop的Attribute将自动添加到根节点的Attribute中:

<header-tab v-bind="info" cos="cos"></header-tab>
image.png

  • 没有单个根节点其实不会被继承:

image.png

禁用Attribute继承和多根节点

  • 如果我们不希望组件的根元素继承attribute,
    • 可以在组件中设置 inheritAttrs: false:
    • 禁用attribute继承的常见情况是需要将attribute应用于根元素之外的其他元素;
    • 可以通过 $attrs来访问所有的 非props的attribute;

image.png

多个根节点的attribute

多个根节点的attribute如果没有显示的绑定,那么会报警告,我们必须手动的指定要绑定到哪一个属性上:
image.png

子组件传递给父组件

  • 什么情况下子组件需要传递内容到父组件呢?
    • 当子组件有一些事件发生的时候,比如在组件中发生了点击,父组件需要切换内容;
    • 子组件有一些内容想要传递给父组件的时候;
  • 我们如何完成上面的操作呢?
    • 首先,我们需要在子组件中定义好在某些情况下触发的事件名称;
    • 其次,在父组件中以v-on的方式传入要监听的事件名称,并且绑定到对应的方法中;
    • 最后,在子组件中发生某个事件的时候,根据事件名称触发对应的事件;

      自定义事件的流程

      封装一个CounterOperation.vue的组件:
      内部其实是监听两个按钮的点击,点击之后通过 this.$emit的方式发出去事件;
      我们需要添加:emits: [“addOne”, “subOne”], ```vue

  1. ```vue
  2. <template>
  3. <div>
  4. Header: {{ num }}
  5. <header-tab @addOne="addOne" @subOne="subOne"></header-tab>
  6. </div>
  7. </template>
  8. <script>
  9. import HeaderTab from "./HeaderTab.vue";
  10. export default {
  11. name: "Header",
  12. components: {
  13. HeaderTab,
  14. },
  15. data() {
  16. return {
  17. num: 0,
  18. };
  19. },
  20. methods: {
  21. addOne() {
  22. this.num++;
  23. },
  24. subOne() {
  25. this.num--;
  26. },
  27. },
  28. };
  29. </script>

image.pngimage.png
image.png
image.png

自定义事件的参数和验证

自定义事件的时候,我们也可以传递一些参数给父组件:

  1. handleClickAdd() {
  2. this.$emit("addOne", 10);
  3. },

在vue3当中,可以对传递的参数进行验证

  1. <template>
  2. <div>
  3. <button @click="handleClickAdd">+</button>
  4. <button @click="handleClickSub">-</button>
  5. <button @click="handleClickAdd10">+10</button>
  6. </div>
  7. </template>
  8. <script>
  9. export default {
  10. name: "HeaderTab",
  11. // emits: ["addOne", "subOne"],
  12. emits: {
  13. addOne: null,
  14. subOne: null,
  15. addTen: function (payload) {
  16. // 这个必须得有个返回值,否则会报错的
  17. if (payload === 10) {
  18. return true;
  19. }
  20. return false;
  21. },
  22. },
  23. methods: {
  24. handleClickAdd() {
  25. this.$emit("addOne");
  26. },
  27. handleClickSub() {
  28. this.$emit("subOne");
  29. },
  30. handleClickAdd10() {
  31. this.$emit("addTen", 8);
  32. },
  33. },
  34. };
  35. </script>