代码示例:
调用签名语法(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 标识符(由字母、数字、下划线、美元符号组成,且不能以数字开头)。例如
confirm、reset、cancel都是合法的标识符,因此可以直接写,无需引号。 -
字符串字面量:当键名包含特殊字符(如冒号
:、连字符-、空格等)时,必须用引号括起来,否则 TypeScript 会报语法错误。"update:visible"和"update:queryDTO"中包含了冒号,不属于合法的标识符,所以必须加上引号。
因此,你的代码中:
-
"update:visible"和"update:queryDTO"用双引号是因为它们包含冒号。 -
confirm、reset、cancel没有用引号是因为它们是合法的标识符,不加引号更简洁。
注意:即使对于合法的标识符,你也可以选择加引号(例如 "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 和文档工具正确解析。



4万+

被折叠的 条评论
为什么被折叠?



