Vue3 defineSlots类型化插槽的TypeScript支持
在 Vue 3.3+ 版本中,defineSlots 宏的引入为组件插槽带来了完整的 TypeScript 类型推断能力。这意味着在父组件中使用插槽时,IDE 可以准确地自动补全插槽暴露的 props(参数),并在类型不匹配时直接报错,无需再手动编写繁琐的类型声明文件。以下步骤将详细演示如何配置和使用这一特性。
1. 创建子组件并定义插槽类型
打开你的项目组件目录,新建或打开一个子组件文件,例如 UserList.vue。我们需要在这个文件中声明插槽将向父组件传递哪些数据。
在 <script setup> 标签内,定义一个描述插槽 props 的接口对象,并将其作为泛型参数传递给 defineSlots。
输入以下代码:
<script setup lang="ts">
// 1. 定义插槽 Props 的类型
interface UserSlotProps {
id: number;
name: string;
isAdmin: boolean;
}
// 2. 使用 defineSlots 宏声明插槽类型
// key 是插槽名称,value 是一个函数,其参数是插槽传递给父组件的 props
defineSlots<{
// 默认插槽,接收 user 数据
default: (props: UserSlotProps) => any;
// 具名插槽 'header',接收 title 数据
header: (props: { title: string }) => any;
}>();
</script>
<template>
<div class="user-list">
<!-- 3. 在模板中正常使用插槽,并绑定数据 -->
<div class="header">
<!-- 注意:这里的 name 对应上面定义的 header 插槽 -->
<slot name="header" title="用户管理系统"></slot>
</div>
<ul>
<li v-for="user in users" :key="user.id">
<!-- 将具体数据传给默认插槽 -->
<slot :user="user"></slot>
</li>
</ul>
</div>
</template>
<script lang="ts">
// 用于演示的模拟数据
export default {
data() {
return {
users: [
{ id: 1, name: 'Alice', isAdmin: true },
{ id: 2, name: 'Bob', isAdmin: false }
]
}
}
}
</script>
2. 在父组件中消费类型化插槽
创建或打开父组件文件,例如 App.vue。当你引入 UserList 组件并在模板中使用 v-slot 或 # 语法时,TypeScript 会自动识别上一步定义的 props 结构。
编写如下代码来体验类型推断:
<script setup lang="ts">
import UserList from './UserList.vue';
</script>
<template>
<UserList>
<!-- 1. 使用具名插槽 'header' -->
<!-- IDE 会自动提示 'title' 参数,且类型为 string -->
<template #header="{ title }">
<h1>{{ title.toUpperCase() }}</h1>
</template>
<!-- 2. 使用默认插槽 -->
<!-- IDE 会自动提示参数名为 'user'(由子组件 slot :user="user" 决定),
且类型为 UserSlotProps (包含 id, name, isAdmin) -->
<template #default="{ user }">
<span class="user-name">{{ user.name }}</span>
<!-- 如果尝试访问 user.age,TypeScript 会报错,因为接口中未定义 -->
<span v-if="user.isAdmin" class="badge">(管理员)</span>
</template>
</UserList>
</template>
观察代码编辑器:当你在 { } 内部输入时,应该能看到 title 和 user 的智能提示。如果将 user.name 修改为 user.age,编辑器会立即显示红色波浪线提示类型错误。
3. 处理可选插槽与插槽检查
在某些场景下,父组件可能不会传入某个具名插槽。defineSlots 返回的对象(如果接收的话)可以用来检查插槽是否存在,虽然通常仅用于模板渲染逻辑,但在 <script setup> 中进行运行时检查也是可行的。
修改子组件 UserList.vue 的 script 部分:
<script setup lang="ts">
// 定义 slots 对象以接收运行时插槽信息
// 注意:泛型定义依然用于类型推断
const slots = defineSlots<{
default: (props: { msg: string }) => any;
footer?: () => any; // 使用 ? 表示该插槽是可选的
}>();
// 示例:根据插槽是否存在执行逻辑
const hasFooter = !!slots.footer;
</script>
注意:在绝大多数情况下,你只需要 defineSlots<{...}>() 这种不带返回值的写法,因为 Vue 的编译器会自动处理类型的注入和模板的检查。只有当你需要在 JS 逻辑中显式判断插槽是否存在时,才需要接收返回值。
4. 严格约束插槽 Props 类型
为了确保父组件传参的绝对安全,可以在定义接口时使用更严格的工具类型。
打开子组件,修改接口定义如下:
<script setup lang="ts">
import type { Slot } from 'vue';
// 使用严格类型,甚至可以限制返回值的 VNode 类型(通常为 any 即可)
defineSlots<{
// 确保 id 必须是 number,且必须有返回值
item: (props: { id: number; content: string }) => Slot;
}>();
</script>
在父组件中,如果尝试给 v-slot 传入选解构参数时使用了错误的类型名(例如写成了 { idd }),TypeScript 会提示 idd 不存在于 item 插槽的 props 中。
确保你的 tsconfig.json 中开启了 strictMode,以获得最佳的检查效果。
{
"compilerOptions": {
"strict": true,
"vueCompilerOptions": {
"target": 3.3
}
}
}
暂无评论,快来抢沙发吧!