Vue 3 的 Composition API
给开发者带来了更强的逻辑组织能力,但很多人用 defineProps
的方式,依然停留在 Vue 2 的"Options 语法心智"。本质上只是把 props: {}
拿出来"提前声明",并没有真正理解它的运行机制、类型推导优势、默认值处理方式、解构陷阱等关键点。
这篇文章不做语法搬运,而是用实战视角,带你了解:defineProps 到底该怎么写,才是专业的 Vue3 写法。
🎯 为什么说你用错了 defineProps?
我们先来看一个常见的 Vue3 组件写法:
xml
<script setup>
const props = defineProps({
title: String,
count: Number
})
</script>
你以为这就完事了?它只是基本写法。但在真实业务中,我们往往会遇到:
- 需要传默认值
- 想要类型推导
- 解构 props 却发现响应性丢失
- TS 类型重复声明,不够优雅
这些问题,defineProps 其实早就帮你解决了,只是你没用对方式。
✅ 正确的三种 defineProps 写法
① 写法一:声明式类型推导(推荐)
typescript
interface Props {
title: string
count?: number
}
const props = defineProps<Props>()
优点:
- 自动获得类型推导
- 在
<script setup lang="ts">
中书写自然 - 可配合
withDefaults
补充默认值
这是 Composition API 的推荐写法,完全由 TypeScript 驱动,而不是运行时校验。
② 写法二:运行时代码校验(Options 式)
php
const props = defineProps({
title: {
type: String,
required: true
},
count: {
type: Number,
default: 0
}
})
优点:
- 保留 Vue2 的 props 校验逻辑
- 更适合 JS-only 项目(不使用 TS)
缺点:
- 类型推导不如泛型直观
- 与
withDefaults
不兼容
③ 写法三:与 withDefaults
配合(实战最常见)
typescript
const props = withDefaults(defineProps<{
title?: string
count?: number
}>(), {
title: '默认标题',
count: 1
})
优势是:
- 既能获得类型推导,又能写默认值
- 不会重复写 default
- 比纯 defineProps 更简洁易维护
注意:withDefaults 只能配合泛型式 defineProps 使用,不能和对象式 props 写法混用。
⚠️ 高发误区警告:你踩过几个?
🚫 误区 1:直接解构 props,响应性丢失
typescript
const { title, count } = defineProps<{ title: string, count: number }>()
上面的写法会让 title
和 count
成为普通变量,不是响应式的。
解决方式 :使用 toRefs
typescript
const props = defineProps<{ title: string, count: number }>()
const { title, count } = toRefs(props)
这样才能在 watch(title, ...)
中有效监听变化。
🚫 误区 2:类型和默认值重复声明
csharp
const props = defineProps({
title: {
type: String as PropType<string>, // 写了类型
default: 'Hello' // 又写默认值
}
})
在 TS 项目中,这种方式显得繁琐且不智能。建议直接用泛型 + withDefaults,让 IDE 自动推导类型。
🚫 误区 3:没有区分"开发期类型检查" vs "运行时校验"
Vue3 的 Props 有两个模式:
- TypeScript 模式:靠 IDE + 编译器
- Options 模式:在浏览器运行时报错
实际推荐:生产环境靠 TypeScript 检查即可,无需运行时 Props 校验,提高性能。
🎯 defineProps 是真正的组件契约声明
在 Vue3 的 <script setup>
中,defineProps
就是你和使用你组件的人之间的契约。
为什么说它是契约?
- 它声明了组件的"输入规范"
- 它决定了类型校验、默认值逻辑
- 它是组件文档的第一手来源
你越是随便写它,越容易在团队协作时踩坑。
💡 defineProps 的进阶技巧:你未必知道的几个点
✔ 你可以在 defineProps 里使用类型别名
matlab
type Size = 'sm' | 'md' | 'lg'
withDefaults(defineProps<{
size?: Size
}>(), {
size: 'md'
})
这是让 props.size
具备完整类型提示的关键方式。
✔ 配合 defineEmits
写法更完整
typescript
const emit = defineEmits<{
(e: 'submit', value: number): void
(e: 'cancel'): void
}>()
这样写出的组件,输入(props)+ 输出(emit)都具备契约,可以被任何 IDE 精确识别。
✔ defineProps 写法决定你能不能使用 Volar 的类型推导
很多人发现 <MyComponent :title="xx" />
里没有类型提示,大概率是你组件没有正确写 defineProps 的泛型。保持结构清晰,是让 IDE 吃得饱的唯一方式。
🚀 小结:defineProps 不只是 props,它是组件健壮性的开端
错误写法 | 问题 |
---|---|
不加泛型 | IDE 无法提示 |
直接解构 | 响应性丢失 |
类型 + default 双声明 | 代码重复、难维护 |
没有 withDefaults | 写默认值繁琐、不能配合类型推导 |
使用 runtime 校验 + TS | 混乱、效率低 |
正确思路是 :在 TypeScript 项目中,尽可能采用 defineProps<T>() + withDefaults()
写法,做到类型明确、默认值清晰、响应式安全。
📌 怎么判断你是否"真的会用 defineProps"?
- ❌ 你写了 defineProps 但 props 解构不响应
- ❌ 你写 default 写得很痛苦
- ❌ 你项目里 props 写法风格混乱
- ❌ 你的组件在 IDE 中没有 props 自动补全
✅ 如果你能做到:
- 使用泛型 +
withDefaults
- 保持 props 和 emits 的契约完整
- 清晰地类型提示和响应性解构
那恭喜你,是真的理解了 Vue3 的组件心智模型。