Vue3+TypeScript 使用 defineEmits 定义 emits

代码示例:

调用签名语法(Call Signature)

// 调用签名语法(Call Signature)
// 类型定义与 emit 的实际调用方式高度一致,例如 emit("update:visible", true)。
// 可以为每个事件添加 JSDoc 注释,便于文档生成。
const emit = defineEmits<{
  /**
   * 更新对话框可见性
   * @param visible 新的可见状态
   */
  (e: "update:visible", visible: boolean): void;
}>();
// 调用签名语法(Call Signature)
// 类型定义与 emit 的实际调用方式高度一致,例如 emit("update:visible", true)。
// 可以为每个事件添加 JSDoc 注释,便于文档生成。
const emit = defineEmits<{
  /**
   * 更新对话框可见性
   * @param visible 新的可见状态
   */
  (e: "update:visible", visible: boolean): void;
  /**
   * 确认分配
   * @param allocateData 分配数据列表
   */
  (e: "confirm", allocateData: CapitalAllocateCreateDTO[]): void;
  /**
   * 取消分配
   */
  (e: "cancel"): void;
}>();

对象字面量语法(Vue 3.3+ 推荐)

// 对象字面量语法(Vue 3.3+ 推荐)
// 更加简洁,尤其适合事件参数简单的情况。
// 对事件参数的校验更严格(元组中的顺序和类型必须匹配)。
const emit = defineEmits<{
  /**
   * 更新对话框可见性
   * @param visible 新的可见状态
   */
  "update:visible": [visible: boolean];
}>();
// 对象字面量语法(Vue 3.3+ 推荐)
// 更加简洁,尤其适合事件参数简单的情况。
// 对事件参数的校验更严格(元组中的顺序和类型必须匹配)。
const emit = defineEmits<{
  /**
   * 更新对话框可见性
   * @param visible 新的可见状态
   */
  "update:visible": [visible: boolean];
  /**
   * 确认分配
   * @param allocateData 分配数据列表
   */
  confirm: [allocateData: CapitalAllocateCreateDTO[]];
  /**
   * 取消分配
   */
  cancel: [];
}>();

vue3 定义 emits,以下这两种方式分别是什么,哪种更好?
方式1:
const emit = defineEmits<{
  (e: "update:visible", visible: boolean): void;
}>();
方式2:
const emit2 = defineEmits<{
  "update:visible": [visible: boolean];
}>();

在 Vue 3 中,使用 <script setup> 时,defineEmits 支持两种 TypeScript 类型标注方式。两者功能完全等价,但语法风格和适用场景略有不同。

方式1:调用签名(Call Signature)语法

typescript

const emit = defineEmits<{
  (e: "update:visible", visible: boolean): void;
  (e: "change", id: number, name: string): void;
}>();
  • 语法特点:采用函数重载的写法,每个事件对应一个函数签名,参数依次为事件名和事件参数。

  • 优势

    • 类型定义与 emit 的实际调用方式高度一致(例如 emit('update:visible', true))。

    • 可以为每个事件添加 JSDoc 注释,便于文档生成。

  • 劣势

    • 当事件较多时,代码略显冗长。

    • 事件参数数量较多时,签名会变长。

方式2:对象字面量语法(Vue 3.3+ 推荐)

typescript

const emit = defineEmits<{
  "update:visible": [visible: boolean];
  "change": [id: number, name: string];
}>();
  • 语法特点:每个事件名作为键,对应的值是一个元组(Tuple)类型,表示该事件的参数列表。

  • 优势

    • 更加简洁,尤其适合事件参数简单的情况。

    • 与 Vue 3.3+ 的新特性对齐,官方文档示例多采用此方式。

    • 对事件参数的校验更严格(元组中的顺序和类型必须匹配)。

  • 劣势

    • 无法直接为每个事件添加注释(但可以在事件名上方添加,不过工具支持可能不如方式1)。

哪种更好?

没有绝对的好坏,取决于团队偏好和项目需求,但以下建议可供参考:

  • 如果你追求简洁且使用 Vue 3.3+,方式2 是官方推荐的新语法,更紧凑,适合大多数场景。

  • 如果你需要为事件添加详细文档(例如为公共组件库编写类型),方式1 可以利用 JSDoc 为每个事件签名添加注释,更清晰。

  • 两种方式可以混用吗? 可以,但建议项目内保持统一。

总结

  • 方式2(对象字面量):简洁、现代,是 Vue 3.3+ 的主流写法。

  • 方式1(调用签名):传统、直观,适合需要详细文档或习惯函数重载的开发者。

在你的 CapitalInfoDialog 组件中,目前使用的是方式1,若想改用方式2,完全可以,功能一致。选择一种并保持一致即可。


使用方式1:调用签名(Call Signature)语法,如何为每个事件添加 JSDoc 注释,便于文档生成

在使用调用签名语法(方式1)定义 emits 时,可以通过在每个函数签名上方添加 JSDoc 注释来为事件添加说明,这些注释会被 TypeScript 保留,并可被文档生成工具(如 TypeDoc、Vuese)识别。

示例代码

typescript

const emit = defineEmits<{
  /**
   * 更新对话框可见性
   * @param visible - 新的可见状态
   */
  (e: "update:visible", visible: boolean): void;

  /**
   * 保存成功事件
   * @param isNew - 是否为新增模式
   * @param id - 保存后的数据 ID(可选)
   */
  (e: "save", isNew: boolean, id?: number): void;

  /**
   * 取消操作事件
   */
  (e: "cancel"): void;
}>();

注释说明

  • 每个事件签名前使用 /** ... */ 多行注释或 // 单行注释(推荐多行,更规范)。

  • 可以在注释中描述事件的作用,并使用 @param 标签说明每个参数的名称和含义(如果参数有名称)。

  • 注意:函数签名中的参数名(如 visible)应与实际使用时的参数对应,这样文档工具才能正确关联。

生成文档

使用支持 TypeScript 的文档生成工具(如 TypeDoc 或 Vuese)时,这些注释会被提取并生成友好的 API 文档。例如,TypeDoc 会为每个事件生成条目,包含注释中的描述和参数列表。

对比方式2(对象字面量语法)

方式2(对象字面量)在添加注释时稍微麻烦一些,因为每个事件是一个属性,其值是元组类型,无法直接在元组上添加注释。通常需要将注释放在事件名上方,但工具支持可能不如方式1直观:

typescript

const emit = defineEmits<{
  /**
   * 更新对话框可见性
   */
  "update:visible": [visible: boolean];
  /**
   * 保存成功事件
   */
  "save": [isNew: boolean, id?: number];
}>();

这种方式中,注释可以放在属性名上方,但参数说明只能通过 @param 在属性注释中描述,而无法像方式1那样精确对应每个参数。

总结

  • 若你重视文档生成和参数说明,方式1(调用签名) 更自然,可以为每个事件及其参数添加详细的 JSDoc。

  • 若你更注重简洁性,方式2也支持简单的事件描述,但参数说明稍显局限。

无论选择哪种,保持项目内风格一致即可。


使用对象字面量语法,不能这样写吗?
const emit = defineEmits<{
  /**
   * 更新对话框可见性
   * @param visible - 新的可见状态
   */
  "update:visible": [visible: boolean];
}>();

你的写法是完全正确的!在 Vue 3 中使用对象字面量语法定义 emits 时,为每个事件属性添加 JSDoc 注释是 TypeScript 支持的特性,并且可以被文档生成工具(如 TypeDoc、Vuese)正常解析。你的示例:

typescript

const emit = defineEmits<{
  /**
   * 更新对话框可见性
   * @param visible - 新的可见状态
   */
  "update:visible": [visible: boolean];
}>();

是有效的,它会为 update:visible 事件生成带有描述的文档,@param 标签也能正确关联到元组中的第一个参数。

两种方式注释的对比

  • 方式1(调用签名):注释写在函数签名上方,每个参数直接出现在签名中,可以使用 @param 标签详细说明。

  • 方式2(对象字面量):注释写在属性名上方,同样可以使用 @param 标签在注释中说明参数含义,虽然参数名称取自元组的标签(如 visible),但效果一致。

实际效果

无论采用哪种方式,生成的文档质量相当。选择哪一种更多是团队约定和个人偏好。Vue 官方在 3.3+ 版本中推荐对象字面量语法,因为它更简洁,且完全支持 TypeScript 的元组标签和注释。

总结

你可以放心使用对象字面量语法并添加注释,它完全满足文档生成的需求。保持项目内风格统一即可。


以下代码:
// 对象字面量语法(Vue 3.3+ 推荐)
// 更加简洁,尤其适合事件参数简单的情况。
// 对事件参数的校验更严格(元组中的顺序和类型必须匹配)。
const emit = defineEmits<{
  /**
   * 更新对话框可见性
   * @param visible 新的可见状态
   */
  "update:visible": [visible: boolean];
  /**
   * 更新查询对象
   * @param queryDTO 新的查询对象
   */
  "update:queryDTO": [queryDTO: CapitalInfoQueryDTO];
  /**
   * 确定查询
   */
  confirm: [];
  /**
   * 重置
   */
  reset: [];
  /**
   * 取消
   */
  cancel: [];
}>();
为什么confirm、reset、cancel没有双引号,其他的就有

在 TypeScript 的对象字面量类型中,键(属性名)可以是标识符(如 confirm)或字符串字面量(如 "update:visible")。两者的主要区别在于:

  • 标识符:必须是合法的 JavaScript 标识符(由字母、数字、下划线、美元符号组成,且不能以数字开头)。例如 confirmresetcancel 都是合法的标识符,因此可以直接写,无需引号。

  • 字符串字面量:当键名包含特殊字符(如冒号 :、连字符 -、空格等)时,必须用引号括起来,否则 TypeScript 会报语法错误。"update:visible" 和 "update:queryDTO" 中包含了冒号,不属于合法的标识符,所以必须加上引号。

因此,你的代码中:

  • "update:visible" 和 "update:queryDTO" 用双引号是因为它们包含冒号。

  • confirmresetcancel 没有用引号是因为它们是合法的标识符,不加引号更简洁。

注意:即使对于合法的标识符,你也可以选择加引号(例如 "confirm": []),但通常不加引号更符合习惯,且代码更简洁。两种写法在功能上完全等价。


defineEmits使用对象字面量语法定义emits时,对于无参的定义,是使用[],还是void

在 Vue 3 中使用对象字面量语法定义 defineEmits 时,对于无参数的事件,应该使用 空元组 [],而不是 void

原因

  • [] 表示事件没有参数:这是一个 TypeScript 元组类型,表示该事件在被 emit 调用时不需要传递任何参数,例如 emit('cancel')

  • void 不合适void 通常用作函数返回值类型,表示“无返回值”,但不能表示事件参数列表为空。如果写成 'cancel': void,TypeScript 会认为事件需要一个 void 类型的参数(即 emit('cancel', undefined)),这与预期不符。

正确示例

typescript

const emit = defineEmits<{
  'update:visible': [visible: boolean];  // 有参数
  'cancel': [];                           // 无参数 ✅
  'save': [isNew: boolean];               // 有参数
}>();

使用方式

  • 有参数emit('update:visible', true)

  • 无参数emit('cancel')

这种写法既符合 TypeScript 类型约束,也能被 Vue 和文档工具正确解析。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值