这一节了解一下Vue3中的defineEmits,defineEmits 是Vue 3.2+引入的编译器宏,仅在<script setup>语法糖中使用,无需从vue导入。 它替代了Options API中的emits选项,返回一个emit函数用于触发事件,简单总结如下:
API:
TypeScript
数组语法
<script setup>
const emit = defineEmits(['submit', 'close', 'update:modelValue'])
</script>
TypeScript
TypeScript泛型语法
<script setup lang="ts">
const emit = defineEmits<{
(e: 'submit', payload: { email: string }): void
(e: 'close'): void
(e: 'update:modelValue', value: string): void
}>()
// Vue 3.3+ 更简洁写法
const emit = defineEmits<{
submit: [payload: { email: string }]
close: []
'update:modelValue': [value: string]
}>()
</script>
场景:
1 父子组件通信:子组件触发事件通知父组件,如表单提交、列表项删除。
2 自定义v-model双向绑定:子组件接收modelValue prop并触发update:modelValue事件。
3 弹窗/模态框状态通知:子组件触发open/close事件通知父组件控制显隐。
栗子:
TypeScript
ChildEmit.vue
<template>
<div class="child">
<p>子组件内部数字:{{ innerNum }}</p>
<button @click="handleAdd">+1,通知父组件</button>
<button @click="handleClose">关闭弹窗</button>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
const emit = defineEmits<{
'update:num': [val: number]
close: []
}>()
const innerNum = ref(0)
const handleAdd = () => {
innerNum.value++
emit('update:num', innerNum.value)
}
const handleClose = () => {
emit('close') // 触发close事件,无参数
}
</script>
TypeScript
父组件 ParentEmit.vue
<template>
<div>
<h2>父组件,接收子组件事件</h2>
<p>父拿到的值:{{ parentNum }}</p>
<ChildEmit @update:num="onUpdateNum" @close="onClose" />
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import ChildEmit from './ChildEmit.vue'
const parentNum = ref(0)
const onUpdateNum = (val: number) => {
parentNum.value = val
console.log('子传过来的新数字:', val)
}
const onClose = () => {
alert('子组件触发关闭事件')
}
</script>
栗子2
TypeScript
子组件 MyDialog.vue
<template>
<div v-if="visible" class="dialog-wrap">
<div class="dialog">
<h3>{{ title }}</h3>
<p>弹窗内容</p>
<button @click="handleConfirm">确定</button>
<button @click="handleCancel">取消</button>
</div>
</div>
</template>
<script setup lang="ts">
interface DialogProps {
visible: boolean
title: string
}
const props = defineProps<DialogProps>()
const emit = defineEmits<{
confirm: []
cancel: []
}>()
const handleConfirm = () => {
emit('confirm')
}
const handleCancel = () => {
emit('cancel')
}
</script>
<style scoped>
.dialog-wrap {
position: fixed;
inset: 0;
background: rgba(0,0,0,0.4);
display:flex;
justify-content:center;
align-items:center;
}
.dialog {
background:#fff;
padding:20px;
border-radius:8px;
}
</style>
TypeScript
父组件 ParentDialog.vue
<template>
<div>
<button @click="dialogVisible = true">打开弹窗</button>
<MyDialog :visible="dialogVisible" title="用户编辑弹窗"
@confirm="onConfirm"
@cancel="onCancel"
/>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import MyDialog from './MyDialog.vue'
const dialogVisible = ref(false)
const onConfirm = () => {
alert('点击确定')
dialogVisible.value = false
}
const onCancel = () => {
alert('点击取消')
dialogVisible.value = false
}
</script>
注意:
1 编译器宏不可导入:直接使用即可,import { defineEmits } from 'vue' 会触发警告。
2 必须顶层调用:只能在 <script setup> 顶层作用域调用,不可置于 if、函数或循环内。
3 每个组件仅调用一次。