一、概念
在 Vue3 的 <script setup>
语法糖中,defineProps
、defineEmits
和 defineExpose
是三个核心 API,用于处理组件的属性接收 、事件触发 和内部成员暴露,是组件通信和封装的基础。
二、说明
defineProps
:声明组件接收的 props
用于在组件中声明可以从父组件接收的属性(props) ,相当于选项式 API 中的 props
选项。
- 特点 :无需导入,直接在
<script setup>
中使用;返回一个包含接收 props 的对象,可通过该对象访问 props 的值。 - 作用:明确组件的输入参数,支持类型校验,提升代码可读性和可维护性。
基本用法
vue
<template>
<div>父组件传递的名称:{{ name }}</div>
<div>年龄:{{ age }}</div>
</template>
<script setup>
// 1. 简单声明(数组形式,仅指定 prop 名称)
// const props = defineProps(['name', 'age'])
// 2. 完整声明(对象形式,指定类型、默认值、校验等)
const props = defineProps({
name: {
type: String,// 类型
required: true, // 是否必传
default: '默认名称' // 默认值(非必传时生效)
},
age: {
type: Number,
validator: (value) => { // 自定义校验
return value >= 0;
// 年龄不能为负数
}
}
})
// 访问 props 的值
console.log(props.name) // 父组件传递的 name
console.log(props.age) // 父组件传递的 age
</script>
TypeScript 支持:
可通过泛型指定类型,实现更严格的类型校验:
typescript
// TypeScript 写法
const props = defineProps<{
name: string;
age?: number; // 可选属性
}>()
// 带默认值的 TypeScript 写法(需配合 withDefaults)
const props = withDefaults(defineProps<{
name: string;
age?: number;
}>(), {
age: 18 // 默认值
})
defineEmits
:声明组件触发的事件
用于在组件中声明可以向父组件触发的事件(emits) ,相当于选项式 API 中的 emits
选项。
- 特点 :无需导入,直接使用;返回一个
emit
函数,用于触发事件。 - 作用:明确组件的输出事件,支持事件参数类型校验,避免事件名冲突
基本使用
vue
<template>
<button @click="handleClick">点击触发事件</button>
</template>
<script setup>
// 1. 简单声明(数组形式,仅指定事件名称)
// const emit = defineEmits(['change', 'submit'])
// 2. 完整声明(对象形式,指定事件参数类型)
const emit = defineEmits({
// 无参数的事件
change: () => true, // 校验通过(始终返回 true 表示无校验)
// 有参数的事件(校验参数是否符合预期)
submit: (username: string, password: string) => {
return username && password; // 仅当 username 和 password 都存在时校验通过
}
})
// 触发事件(通过返回的 emit 函数)
const handleClick = () => {
emit('change') // 触发无参事件
emit('submit', 'admin', '123456') // 触发带参事件
}
</script>
TypeScript 支持:
通过泛型指定事件类型,获得更精准的类型提示:
typescript
// TypeScript 写法(声明事件名和参数类型)
const emit = defineEmits<{
(e: 'change'): void; // 无参数事件
(e: 'submit', username: string, password: string): void; // 带参数事件
}>()
// 触发时会有类型提示
emit('submit', 'admin') // 错误:缺少 password 参数(TypeScript 会报错)
defineExpose
:暴露组件内部成员
用于在 <script setup>
中显式暴露组件的内部属性或方法 ,让父组件可以通过 ref
访问。
- 背景 :在
<script setup>
中,组件的内部成员(变量、函数)默认是 "私有" 的,父组件无法通过ref
直接访问。 - 作用:主动暴露需要被父组件访问的成员,实现父子组件的 "直接通信"(非 props/emits 方式)。
基本语法
Child.vue
<!-- 子组件 Child.vue -->
<template>
<div>子组件内部值:{{ count }}</div>
</template>
<script setup>
import { ref } from 'vue'
// 内部状态和方法 const count = ref(0)
const increment = () => {
count.value++
}
// 暴露给父组件(父组件可通过 ref 访问)
defineExpose({
count,
increment
})
</script>
Parent.vue
<!-- 父组件 Parent.vue -->
<template>
<Child ref="childRef" />
<button @click="accessChild">访问子组件</button>
</template>
<script setup>
import { ref } from 'vue'
import Child from './Child.vue'
const childRef = ref(null)
const accessChild = () => {
// 通过 ref 访问子组件暴露的成员
console.log(childRef.value.count) // 0(访问子组件的 count)
childRef.value.increment() // 调用子组件的 increment 方法
}
</script>
三、总结
API | 作用 | 核心场景 |
---|---|---|
defineProps |
声明组件接收的 props | 父组件向子组件传递数据 |
defineEmits |
声明组件触发的事件,返回 emit 函数 |
子组件向父组件传递数据 / 通知 |
defineExpose |
暴露组件内部成员(属性 / 方法) | 父组件直接访问子组件内部状态 |