Vue3.0组件之间通信(defineProps 和 defineEmits 及 defineExpose)

前言:

  • 一、父传子 defineProps
  • 二、子传父 defineEmits
  • 三、子组件暴露属性和方法给父组件 defineExpose
  • 四、依赖注入Provide / Inject

在 <script setup> 中必须使用 defineProps 和 defineEmits API 来声明 props 和 emits ,它们具备完整的类型推断并且在 <script setup> 中是直接可用的。

<template> 
   <!-- 在模板中直接使用 props 中声明的变量 --> 
   <h1>{{ msg }}</h1> 
   <div>{{ title }}</div> 
</template>

<script setup>
const props = defineProps({ 
  msg: String, 
  title: { 
    type: String, 
    default: '我是标题' 
  }, 
  list: { 
    type: Array, 
    default: () => [] 
  } 
});
console.log(props.msg);   //使用props中的属性

const emits = defineEmits(['change', 'delete']);
</script>
View Code

1. defineProps defineEmits 、defineExpose 都是只能在 <script setup> 中才能使用,他们不需要被导入即可使用,并且会在编译 <script setup> 语法块时一同被编译。

2. defineProps 接收与 props 选项相同的值,defineEmits 也接收 emits 选项相同的。

defineProps 是Vue3的写法并且是一个仅在 <script setup> 中可使用的编译命令,并不需要显式地导入;在Vue3的非语法糖setup和在Vue2中的写法是 props 。

注意:defineProps() 中的参数不可以访问 <script setup> 中定义的其他变量,因为在编译时整个表达式都会被移到外部的函数中。
defineProps 只能在 setup 中使用,且只能在 setup 的顶层使用,不能在局部作用域使用。
和 vue2 一样,defineProps 里的属性只读,不能修改。

在子组件中可以使用defineProps声明需要接收父组件的哪些props,它需要定义一个包含props字段的对象,每个字段定义默认值和类型等信息。当父组件的props发生变化时,子组件也会随之响应。

defineProps支持的主要类型有:

  • String
  • Number
  • Boolean
  • Object
  • Array
  • Function

同时也支持许多高级类型,比如,枚举类型,对象类型,联合类型等等。

我们可以对props进行验证,确保传入的值符合我们期望的值。

  • type:定义数据的类型
  • required:是否必须
  • default:默认值
  • validator:自定义验证
 const props = defineProps({
    message: {
      type: String,
      default: ''
    },
    count: {
      type: Number,
      required: true,
      default: 0,
      validator: (value) => {
        return value >= 0 && value <= 10
      }
    }
    type:{
        type: String,
        validator: (value) => {
          return ['success', 'warning', 'danger', 'info'].includes(value)
        }
    },      
    data:{
      type: [Array, Object],
      default: () => {
         return { name: 'jack', age: 20 }
      }
    }
 })
View Code

实例:父组件传值给子组件

1、定义子组件 

<template>
  我是子组件
  <p>子组件得到的name:{{ props.name }}</p>
  <p>子组件得到的age:{{ props.age }}</p>
  <!--可以省略前面的props直接写属性名--->
  <p>子组件得到的name:{{ name }}</p>
  <p>子组件得到的age:{{ age }}</p>
</template>

<script setup>
//方式1: 以对象的形式去接收
const props = defineProps({
  name: {
    type: String,
    default: "张三",
  },
  age: {
    type: Number,
    default: 22
  }
});

// props.age = 18 // 报错,注意 defineProps 中的属性只读不能修改

//方式2: 以数组的方式去接收
//const childProps = defineProps(['name', 'age']);
</script>

2、定义父组件 

<template>
  name: {{ name }} <br />
  age: {{ age }} <br />
  <Child :name="name" :age="age" />
</template>

<script setup>
import { ref } from 'vue'
import Child from './components/Child.vue'

const name = ref('LiuQing');
const age = ref(18);
</script>

defineEmits 和 defineProps 一样也是Vue3的写法并且仅用于 <script setup> 中,并且不需要导入;在Vue3的非语法糖setup和在Vue2中的写法是 emits 。

defineEmits 的不同点在于,组件要触发的事件可以显式地通过 defineEmits() 来声明。

defineEmits 用于子组件向父组件传递消息,在父组件中,只需要监听子组件的自定义事件,然后执行相应的逻辑即可。

注意:如果一个原生事件的名字 (例如 click) 被定义在 emits 选项中,则监听器只会监听组件触发的 click 事件而不会再响应原生的 click 事件。

实例:子组件向父组件传值

1、定义子组件

// 子组件 child.vue 
<template>
  <button @click="handelClick">传递给父级</button>
  <button @click="add">加</button>
  <button @click="decrease">减</button>
</template>
 
<script setup>
const emits = defineEmits(['clickFn', 'add', 'decrease'])// 定义一个或多个自定义事件

// 触发emits事件 const handelClick = () => { emits('clickFn', { name: '张三', age: 18, id: 1 }) // 第一个参数为自定义事件名 第二个参数为要传递的数据 } const add = () => { emits('add', 10) // 第一个参数为自定义事件名 第二个参数为要传递的数据 } const decrease = () => { emits('decrease', 3) // 第一个参数为自定义事件名 第二个参数为要传递的数据 } </script>

2、定义父组件

// 父组件 parent.vue 
<template>
  <h3>年龄:{{ age }}</h3>
  <child :name="name" @clickFn="updateInfo" />
</template>
 
<script setup>
import { ref } from 'vue'
import child from './components/child.vue'

const name = ref('李四');
const age = ref(10);

const updateInfo = (obj) => {
  console.log(obj) // { name: '张三', age: 18, id: 1 }
  name.value = obj.name;
  age.value = obj.age;  
}
</script>

defineExpose 是Vue3中的一个新API,它允许子组件暴露其内部属性或方法给父组件访问。可以通过将属性或方法添加到defineExpose函数中来实现。

获取用setup语法糖创建的子组件实例时,获取的实例是没有子组件自定义的属性和方法的,此时我们需要通过defineExpose来暴露子组件的属性和方法。

在父组件中,我们使用ref属性引用了子组件。需要注意的是,defineExpose函数必须在setup函数中调用,否则会报错。

1、定义子组件 

<template>
   <p>子组件test</p>
</template>

<script setup>
import { ref } from 'vue'

const msg = ref('Hello Vue3')

const a = () => {
  console.log(1)
}

const handleChangeMsg = (v) => { 
  msg.value = v 
}

defineExpose({
    msg, 
    a, 
    handleChangeMsg 
})
</script>

2、定义父组件

<template>
   <Test ref="testInstanceRef" />
   <button @click="handleChangeMsg">handleChangeMsg</button>
</template>

<script setup>
import { ref, onMounted } from "vue";
import Test from "./components/Test.vue";

const testInstanceRef = ref();

onMounted(() => {
   const testInstance = testInstanceRef.value;
   console.log(testInstance.$el)   // p标签
   console.log(testInstance.msg)   // msg属性
   console.log(testInstance.a)    // a方法,如果不使用defineExpose暴露是拿不到的
})

const handleChangeMsg = () => { 
   testInstanceRef.value.handleChangeMsg('Hello TS') 
}
</script>

 

posted @ 2023-10-11 19:22  以德为先  阅读(6588)  评论(0编辑  收藏  举报