📊 withDefaults 的作用
1. 为 TypeScript props 设置运行时默认值
// TypeScript 定义接口
interface Props {
title?: string
count?: number
}
// 但 TypeScript 的 ? 只表示"可选",不提供运行时默认值
// withDefaults 提供运行时默认值
const props = withDefaults(defineProps<Props>(), {
title: '默认标题', // 运行时:如果没传 title,使用这个值
count: 0 // 运行时:如果没传 count,使用 0
})
2. 保持类型安全
// TypeScript 能检查默认值类型
const props = withDefaults(defineProps<{
title?: string
count?: number
}>(), {
title: '默认标题', // ✅ 正确:string
count: 0, // ✅ 正确:number
// flag: true // ❌ 错误:没有定义 flag
})
🆚 与 Options API 对比
Options API 写法
<script lang="ts">
export default {
props: {
title: {
type: String,
default: '默认标题'
},
count: {
type: Number,
default: 0
}
}
}
</script>
Composition API + <script setup> 写法
<script setup lang="ts">
import { defineProps, withDefaults } from 'vue'
interface Props {
title?: string
count?: number
}
const props = withDefaults(defineProps<Props>(), {
title: '默认标题',
count: 0
})
</script>
📈 高级用法
1. 函数默认值
// 使用函数作为默认值
const props = withDefaults(defineProps<{
data?: Record<string, any>
items?: string[]
}>(), {
data: () => ({}), // 每次返回新对象
items: () => [] // 每次返回新数组
})
2. 复杂对象默认值
interface DeviceConfig {
name: string
interval: number
protocol: 'mqtt' | 'http' | 'websocket'
}
const props = withDefaults(defineProps<{
config?: DeviceConfig
}>(), {
config: () => ({
name: '默认设备',
interval: 5000,
protocol: 'mqtt'
})
})
3. 依赖默认值
// 一个默认值依赖另一个
const props = withDefaults(defineProps<{
size?: 'small' | 'medium' | 'large'
padding?: number
}>(), {
size: 'medium',
// 根据 size 计算 padding
padding: (props) => {
switch (props.size) {
case 'small': return 4
case 'medium': return 8
case 'large': return 16
}
}
})
🔄 编译转换
TypeScript 源码
const props = withDefaults(defineProps<{
title?: string
count?: number
}>(), {
title: '默认标题',
count: 0
})
编译后的 JavaScript
const props = defineProps({
title: { type: String, default: '默认标题' },
count: { type: Number, default: 0 }
})
⚡ 与纯 TypeScript 的区别
| 特性 | TypeScript ? |
withDefaults |
|---|---|---|
| 编译时 | 类型检查 | 类型检查 + 运行时默认值 |
| 运行时 | 无默认值 | 有默认值 |
| 用途 | 类型声明 | 运行时行为 |
// 只有 TypeScript
const props = defineProps<{ title?: string }>()
// 编译时:title 可选
// 运行时:如果没有传 title,props.title 是 undefined
// TypeScript + withDefaults
const props = withDefaults(defineProps<{ title?: string }>(), {
title: '默认标题'
})
// 编译时:title 可选
// 运行时:如果没有传 title,props.title 是 '默认标题'
📊 Vue 3 官方 API 家族
| 类别 | API | 说明 |
|---|---|---|
| 响应式 | ref(), reactive() |
创建响应式数据 |
| 计算 | computed() |
计算属性 |
| 监听 | watch(), watchEffect() |
监听变化 |
| 生命周期 | onMounted(), onUnmounted() |
生命周期钩子 |
| 依赖注入 | provide(), inject() |
跨组件通信 |
| Props | defineProps(), withDefaults() |
组件属性 |
| 事件 | defineEmits() |
自定义事件 |
| 暴露 | defineExpose() |
暴露组件方法 |
💡 为什么重要?
1. 类型安全
// 完整的类型推断
const props = withDefaults(defineProps<{ count?: number }>(), {
count: 0
})
// props.count 类型是 number,不是 number | undefined
// 因为有默认值,所以不会是 undefined
2. 更好的开发体验
// IDE 智能提示
props.count // 提示类型是 number
props.title // 提示类型是 string
3. 减少运行时错误
// 没有默认值
console.log(props.title.toUpperCase()) // 可能报错:undefined
// 有默认值
console.log(props.title.toUpperCase()) // 安全:总是有值
📋 总结
withDefaults 是 Vue 3 的官方 API:
- ✅ 官方提供:Vue 3 组合式 API 的一部分
- ✅ TypeScript 支持:为 defineProps 提供运行时默认值
- ✅ 编译转换:编译为标准的 props 配置
- ✅ 类型安全:保持完整的类型推断
- ✅ 开发体验:提供更好的开发工具支持
在 一些项目中,withDefaults 是定义组件 props 的标准方式,结合了 TypeScript 的类型安全和 Vue 的运行时特性。
