一、基本用法

  1. 声明事件
    使用 defineEmits 定义组件可触发的事件,返回一个 emit 函数用于触发事件:

    vue

    <script setup>
    const emit = defineEmits(['submit', 'update:value'])
    </script>
const emit = defineEmits(["update:modelValue"]);
  1. 触发事件
    在需要的地方调用 emit(eventName, ...args):

    javascript

    function handleSubmit() {
      emit('submit', { data: 'example' })
    }

二、类型检查(TypeScript)

通过泛型参数定义事件签名,增强类型安全:

vue

<script setup lang="ts">
const emit = defineEmits<{
  (e: 'submit', payload: { data: string }): void
  (e: 'update:value', id: number): void
}>()
</script>

或使用更简洁的语法(Vue 3.3+):

typescript

defineEmits<{
  submit: [payload: { data: string }]
  'update:value': [id: number]
}>()
const emit = defineEmits<{
  "update:modelValue": [value: ApplyBasicInfo];
}>();

三种等价的写法:每种写法分别用 kebab-case 和 camelCase 进行事件命名,建议统一采用 kebab-case 命名事件(Vue 官方推荐)

// 1、Example runtime declaration:
const emit = defineEmits(["applyItemSelectionChange", "applyItemXSelectionChange"]);
const emit = defineEmits(["apply-item-selection-change","apply-item-x-selection-change"]);

// 2、Example type-based declaration:
const emit = defineEmits<{
  applyItemSelectionChange: [newSelection: ApplyItem[]];
  applyItemXSelectionChange: [newSelection: ApplyItem[]];
}>();
const emit = defineEmits<{
  "apply-item-selection-change": [newSelection: ApplyItem[]];
  "apply-item-x-selection-change": [newSelection: ApplyItem[]];
}>();

// 3、
const emit = defineEmits<{
  (evt: "applyItemSelectionChange", newSelection: ApplyItem[]): void;
  (evt: "applyItemXSelectionChange", newSelection: ApplyItem[]): void;
}>();
const emit = defineEmits<{
  (evt: "apply-item-selection-change", newSelection: ApplyItem[]): void;
  (evt: "apply-item-x-selection-change", newSelection: ApplyItem[]): void;
}>();


// 触发事件
emit("applyItemSelectionChange", newSelection);
emit("applyItemXSelectionChange", newSelection);

emit("apply-item-selection-change", newSelection);
emit("apply-item-x-selection-change", newSelection);

模板引用:不管用 kebab-case 还是 camelCase 命名事件,在模板中使用 kebab-case 监听事件,如:@apply-item-selection-change 和 @apply-item-x-selection-change

<CommonApplyItemTable
  :apply-item-list="applyItemTableData"
  :data-type="apply.dataType === 1 ? 1 : 2"
  operate-command-type="info-add"
  @apply-item-selection-change="handleApplyItemSelectionChange"
  @apply-item-x-selection-change="handleApplyItemXSelectionChange" />

三、示例场景

1. 表单提交

vue

<script setup>
const emit = defineEmits(['submit'])

function onSubmit() {
  emit('submit', { username: 'user', password: 'pass' })
}
</script>

<template>
  <form @submit.prevent="onSubmit">
    <!-- 表单内容 -->
  </form>
</template>
2. 更新父组件状态

vue

<script setup>
const emit = defineEmits(['update:count'])

function increment() {
  emit('update:count', 10) // 通知父组件更新 count
}
</script>

四、注意事项

  1. 命名规范
    建议使用 kebab-case(如 update:value),因 HTML 属性不区分大小写。

  2. 验证事件参数
    可在 defineEmits 中定义类型(TypeScript)或在 Options API 的 emits 选项添加验证函数:

    javascript

    // Options API 验证
    emits: {
      submit: (payload) => !!payload.data
    }
  3. 与 Options API 对比

    • <script setup> 中用 defineEmits 替代 emits 选项。

    • 类型推断更直观,适合 TypeScript。

  4. 无 <script setup> 时的用法
    需改用 Options API 的 emits 选项:

    javascript

    export default {
      emits: ['submit'],
      setup(props, { emit }) {
        // 使用 emit('submit')
      }
    }

五、总结

  • defineEmits 用于在组合式 API 中声明组件事件。

  • 通过类型泛型(TS)或数组(JS)定义事件列表。

  • 触发事件:emit('event-name', payload)

  • 优势:明确的类型检查和更好的代码可维护性。

在 Vue 3 + TypeScript 中,使用 defineEmits 可以为组件定义强类型的事件。以下是几种常见的使用方式:


基础示例

vue

<script setup lang="ts">
// 子组件 ChildComponent.vue
const emit = defineEmits<{
  // 无参数事件
  (e: 'click'): void
  
  // 带参数事件
  (e: 'submit', payload: { email: string; password: string }): void
  
  // 可选参数事件
  (e: 'update', value?: number): void
}>()

// 触发事件
const handleClick = () => emit('click')
const handleSubmit = () => emit('submit', { email: 'test@a.com', password: '123' })
</script>

更简洁的类型声明(Vue 3.3+ 推荐)

vue

<script setup lang="ts">
// 使用 TypeScript 接口定义事件
interface Emits {
  (e: 'change', value: string): void
  (e: 'toggle', checked: boolean): void
}

const emit = defineEmits<Emits>()

// 触发事件
emit('change', 'new value')
emit('toggle', true)
</script>

结合运行时验证(可选)

vue

<script setup lang="ts">
const emit = defineEmits({
  // 运行时验证函数
  submit: (payload: { email: string }) => {
    if (!payload.email.includes('@')) {
      console.warn('Invalid email format')
      return false
    }
    return true // 返回 true 表示验证通过
  }
})

// 触发事件(如果验证失败,控制台会警告)
emit('submit', { email: 'test@a.com' })
</script>

父组件调用示例

vue

<!-- 父组件 ParentComponent.vue -->
<template>
  <ChildComponent 
    @click="handleChildClick"
    @submit="handleChildSubmit"
  />
</template>

<script setup lang="ts">
const handleChildClick = () => {
  console.log('收到点击事件')
}

const handleChildSubmit = (payload: { email: string; password: string }) => {
  console.log('收到提交数据:', payload)
}
</script>

注意事项

  1. 类型安全:使用 TypeScript 接口/类型定义事件参数,确保触发事件时传递正确的参数类型

  2. Vue 版本:类型声明语法需要 Vue 3.3+ 版本支持

  3. 两种风格:

    • 纯类型声明(defineEmits<{...}>())提供更好的 TS 支持

    • 运行时声明(defineEmits({...}))可以添加验证逻辑

  4. 事件命名:推荐使用 camelCase 事件名(Vue 会自动转换为 kebab-case 在模板中使用)

通过这种方式,可以在组件间获得完整的类型提示和校验,提升代码可靠性。

Logo

北京人形旗下天工造物具身智能开源社区,聚焦具身天工与慧思开物两大平台

更多推荐