vue3中的子父组件传参

980 阅读4分钟

后面会用到的知识点(补充)

后缀运算符

在Vue 3中,有两个后缀运算符:?!,分别表示可选属性和非空断言。

?可选属性:表示该属性是可选的,如果该属性不存在,不会引发错误。

const message = { text: 'Hello World' }
const greeting = message.text?.toUpperCase() // greeting = 'HELLO WORLD'
const emptyMessage = {}
const emptyGreeting = emptyMessage.text?.toUpperCase() // emptyGreeting = undefined

在上面的代码中,当message对象存在text属性时,可以将text属性的值转换为大写字母。而当emptyMessage对象不存在text属性时,使用?.运算符访问text属性不会引发错误,返回值为undefined

!非空断言:表示该属性一定存在,如果该属性不存在,则会引发错误。

const message = { text: 'Hello World' }
const greeting = message.text!.toUpperCase() // greeting = 'HELLO WORLD'
const emptyMessage = {}
const emptyGreeting = emptyMessage.text!.toUpperCase() // TypeError: Cannot read property 'toUpperCase' of undefined

在上面的代码中,当message对象存在text属性时,可以将text属性的值转换为大写字母。而当emptyMessage对象不存在text属性时,使用!运算符访问text属性会引发错误,因为该属性不存在。

在vue中,子父组件传参一直都是非常重要的

父组件向子组件传参(父---->子)

父组件通过 :传参名=“传递的数据” 向子组件传递参数

子组件通过 defineProps<{接收的数据}>() 来接收数据

在 script setup 中,引入的组件会自动注册,所以可以直接使用,无需再通过components进行注册

父组件

    <template>
      <div>父组件数据</div>
      <br>
      <div>姓名:{{ data.name }}</div>
      <br>
      <div>年龄:{{ data.age }}</div>
      <br>
      <div>================================</div>
      <br>
      // 在 script setup 中,引入的组件会自动注册,所以可以直接使用,无需再通过components进行注册
      <Child :name="data.name" :age="data.age"></Child>
    </template>
    
      <script setup lang="ts">
        // 引入子组件
        import Child from './Child.vue'
        // 引入方法
        import {reactive} from 'vue'
        
        const data = reactive({
          name: '大哥',
          age:20
        })
      </script>

子组件

方法一:

    <template>
        <div>子组件数据</div>
        <br>
        <div>姓名:{{ name }}</div>
        <br>
        <div>年龄:{{ age }}</div>
        <br>
    </template>
    
      <script setup lang="ts">
       // 接收数据,(写法一)
        defineProps<{
            name: string,
            age:number
        }>()
        
        // 接收数据, (写法二)
        defineProps({
            name: string,
            age:number
        })
        
        注意:在script中使用接收的数据,要先接收;template中不用
        const props = defineProps({
            name: string,
            age:number
        })
      </script>

方法二:

    <template>
        <div>子组件数据</div>
        <br>
        <div>姓名:{{ name }}</div>
        <br>
        <div>年龄:{{ age }}</div>
        <br>
    </template>
    
      <script setup lang="ts">
       // 接收数据
        // defineProps<{
        //     name: string,
        //     age:number
        // }>()
        
        // 定义接收数据的类型
        type Props = {
            name?: string,
            age?: number
            data?:Object
        }
        // 使用 withDefaults 定义默认值
        withDefaults(defineProps<Props>(), {
            name:'二弟',
            age: 10,
        })
      </script>

结果:

image.png

注入式父子组件传参

父组件可以向子组件(无论层级)注入依赖,每个子组件都可以获得这个依赖,无论层级。

父组件

<script setup>
import child1 from "./child.vue";
import {reactive} from 'vue'
  provide() {
    return { parentData: data.msg };
  },
  const data = reactive({
      msg: "我是父组件的数据",
  })
</script>

子组件

<template>
    <p>我是子组件</p>
    <p>parent组件数据:{{parentData}}</p>
</template>

<script setup>
  inject: ["parentData"],
  
  mounted() {
    console.log('父辈组件的数据',parentData)
  },
</script>

子组件向父组件传参(子---->父)

在Vue 3中,可以使用 defineEmits 函数来声明子组件可以触发的事件。该函数需要在子组件中使用,并且需要在 setup 函数中调用

子组件

<template>
    <div>子组件数据</div>
    <br>
    <button @click="changeMsg">向父组件传参</button>
    <br>
    <div>姓名:{{ name }}</div>
    <br>
    <div>年龄:{{ age }}</div>
    <br>
</template>
<script lang="ts" setup>
import { onMounted } from "vue";
import { ref } from "vue";
const sex = ref('男')
// defineEmits 函数来声明子组件可以触发的事件
// 语法:const 事件名 = defineEmits(['事件'])
const emits = defineEmits(['changeMsg'])
 // 点击事件changeMsg
const changeMsg = () => { 
        // 用注册好的事件,向父组件传参
        // 语法:声明的事件名('事件',递的数据)
        emits('changeMsg',sex)
    }

</script>

父组件

<template>
  <div>父组件数据</div>
  <br>
  <div>姓名:{{ data.name }}</div>
  <br>
  <div>年龄:{{ data.age }}</div>
  <br>
  <div>年龄:{{ data.sex }}</div>
  <br>
  <div>================================</div>
  <br>
  <Child :name="data.name" :age="data.age" @changeMsg="changeMsg"></Child>
</template>

<script setup lang="ts">
import Child from './Child.vue'
import {reactive} from 'vue'
const data = reactive({
  name: '大哥',
  age: 20,
  sex:''
})
// 形参要有数据类型
const changeMsg = (message: string) => {
  data.sex = message
}
</script>

结果:

image.png

defineExpose 获取子组件的实例和内部属性

在vue2中,通常会在子组件便签上加,ref来获取子组件的实例和属性方法,在 Vue3的script-setup 模式下,所有数据只是默认 return 给 template 使用,不会暴露到组件外,所以父组件是无法直接通过挂载 ref 变量获取子组件的数据。

如果要调用子组件的数据,需要先在子组件显示的暴露出来,才能够正确的拿到,defineExpose可以实现

子组件

<template>
  <p>{{name}}</p>
</template>

<script lang="ts" setup>
import { ref } from 'vue'

const name = ref('张麻子')
const changeName = () => {
  name.value = '县长'
}
// 将方法、变量暴露给父组件使用,父组件才可通过 ref API拿到子组件暴露的数据
defineExpose({
  name,
  changeName
})
</script>

父组件

<template>
  <div>
    <child ref='childRef' />
    <button @click="getName">获取子组件中的数据</button>
  </div>
</template>

<script lang="ts" setup>
    import { ref } from 'vue'
    import child from './Child.vue'

    // 子组件ref(TypeScript语法)
    const childRef = ref(null)

    const getName = () => {
      // 获取子组件name
      console.log(childRef.value!.name)
      // 执行子组件方法
      childRef.value?.changeName()
      // 获取修改后的name
      console.log(childRef.value!.name)
    }
</script>