vue3 - 父子组件通信

194 阅读3分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第15天,点击查看活动详情

如果我们一个应用程序将所有的逻辑都放在一个组件中,那么这个组件就会变成非常的臃 肿和难以维护

所以我们必然会进行组件的拆分,最后再将这些组件组合嵌套在一起,最终形成我们的应用程序

image.png

此时必然就需要进行组件和组件间的数据通信, 也就是数据传递

父组件将对应的数据以props的方式传递给子组件

子组件向父组件传递事件,以在父组件中统一对数据进行处理

image.png

父传子

在开发中很常见的就是父子组件之间通信,比如父组件有一些数据,需要子组件来进行展示

这个时候我们可以通过props来完成组件之间的通信

Props是你可以在组件上注册一些自定义的attribute

父组件给这些attribute赋值,子组件通过attribute的名称获取到对应的值

使用方式一 - 数组形式

字符串数组,数组中的字符串就是attribute的名称

父组件

<!-- 组件没有插槽的时候,可以写成单标签, 但必须严格闭合 -->
<Child name="Klaus" age="23" />

子组件

<template>
  <div>
    <div>name: {{ name }}</div>
    <div>age: {{ age }}</div>
  </div>
</template>

<script>
export default {
  // 使用props接收父组件传递过来的props
  props: ['name', 'age']
}
</script>

使用方式二 - 对象方式

对象类型,对象类型我们可以在指定attribute名称的同时,指定它需要传递的类型、是否是必须的、默认值等等

数组用法中我们只能说明传入的attribute的名称,并不能对其进行任何形式的限制

当使用对象语法的时候,我们可以对传入的内容限制更多

  1. 指定传入的attribute的类型
  2. 指定传入的attribute是否是必传的
  3. 指定没有传入时,attribute的默认值

父组件

<!--
  当属性前面加上v-bind的时候
  属性的值即可以是任何的JS变量或合法的js表达式
  所以可以使用:age="23"的形式 为prop传入一个number类型的值
-->
<Child name="Klaus" :age="23" height="1.88" />

子组件

export default {
    props: {
      // name必须传入一个字符串类型的值
      name: String,
      // age必须是number类型的是,且是必传值
      // 但age属性为必传值的时候,就没有必要在设置props的默认值了
      age: {
        type: Number,
        required: true
      },
      height: {
        type: String,
        // 设置默认值 - 当且仅当属性值为undefined的时候,就会使用对应的默认值
        default: '1.98'
      }
    }
}

prop可以选取的值

  1. String
  2. Number
  3. Boolean
  4. Array
  5. Object
  6. Date
  7. Function
  8. Symbol

prop的其它写法

export default {
    props: {
        // propA的值即可是Number类型值,也可以是String类型值
        propA: [Number, String]
    },

    // 如果prop的类型是除Function外的引用类型, 如Array,Object等
    // 那么为了避免多个组件在使用默认prop值的时候
    // 因为是同一个引用对象而产生冲突问题
    // 所以对于这类prop,在设置默认值的时候
    // 值必须是一个返回默认值的函数
    propB: {
        type: Array,
        default: () => [1, 2, 3]
    },

    // 因为函数类型更多的是用于执行而不是使用值
    // 所以对于Function类型的prop,在设置默认值的时候并不需要使用函数
    propC: {
        type: Function,
        default: () => {}
    },

    // 自定义校验规则
    propD: {
        // 会将值以参数的形式传入validator函数中
        // 该函数需要返回一个boolean类型的值
        // 当该函数的返回值为true的时候校验通过,否则校验失败
        validator(v) {
                return v % 2
        }
    }
}

大小写

<!--
    如果属性值为小驼峰形式的时候
    在传递props的时候 
    和组件名一样, prop名称 既可以使用小驼峰写法,也可以使用中划线形式
-->
<Child userName="Klaus" />
<Child user-name="Klaus" />

非prop的attribute

当我们传递给一个组件某个属性,但是该属性并没有定义对应的props或者emits时,就称之为 非Prop的Attribute,常见的包括class、style、id属性等

当组件有单个根节点时,非Prop的Attribute将自动继承到根节点上

父组件

<Child name="Klaus" age="32" />

子组件

<div>Child</div>

渲染后

<div name="Klaus" age="32"> Child </div>

如果我们不希望组件的根元素继承attribute,可以在组件中设置 inheritAttrs: false

此时可以通过 $attrs来访问所有的 非props的attribute

并将所有非props的attribute应用于根元素之外的其他元素

<template>
    <div class="child">
        <div v-bind="$attrs">Child</div>
    </div>
</template>

<script>
export default {
	inheritAttrs: false
}
</script>

如果存在子元素存在多个根节点的时候,如果没有显示的绑定,那么会报警告,必须手动的指定要绑定到哪一个属性上

<template>
    <div v-bind="$attrs" class="child1">Child1</div>
    <div class="child2">child2</div>
</template>

子传父

当子组件有一些事件发生的时候,比如在组件中发生了点击,父组件需要切换内容

子组件有一些内容想要传递给父组件的时候,我们都需要将数据从子组件传递给父组件

父组件

<template>
   <div>
    <!-- 在组件上监听自定义事件,并绑定对应的事件处理函数 -->
    <Child @childEvent="handleEvent" />
   </div>
</template>

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

export default {
    components: {
       Child
    },

    methods: {
        handleEvent(...payload) {
            console.log(payload)
        }
    }
}
</script>

子组件

<template>
    <!-- 在子组件中监听对应的方法,响应对应的事件  -->
    <button @click="handleClick">click me</button>
</template>

<script>
export default {
    methods: {
        handleClick() {
            // 通过$emit触发一个事件
            // 参数一 - 自定义事件名
            // 后续参数 - 参数列表
            this.$emit('childEvent', 10, 20)
        }
    }
}
</script>

参数验证

export default {
    // 自vue3开始,所有的事件都必须在emits中进行声明
    // 不声明,也不会存在对应的问题
    // 但是不声明的事件,会存在于$attrs中
    // 所以推荐对使用到的自定义事件在emits中进行声明
    emits: ['childEvent'],
    methods: {
        handleClick() {
            this.$emit('childEvent', 10, 20)
        }
    }
}

emits选项除了上述的数组写法,还存在对象的写法

通过对象语法,可以对自定义事件中所传入的参数进行自定义校验

emits: {
  // 当属性值为null的时候,表示不进行任何的校验
  // 此时的写法和数组写法是一致的
  childEvent:  null
}
emits: {
  // 自定义事件传入了几个参数,在自定义校验函数中,就可以获取到几个对应参数
  // 该自定义事件需要返回一个boolean类型值,如果为true表示校验通过
  // 如果为false,校验失败,事件依旧会成功执行,只是会在控制台中出现警告
  childEvent(num1, num2) {
    return num1 >= num2
  }
}