📊 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

  1. ✅ 官方提供:Vue 3 组合式 API 的一部分
  2. ✅ TypeScript 支持:为 defineProps 提供运行时默认值
  3. ✅ 编译转换:编译为标准的 props 配置
  4. ✅ 类型安全:保持完整的类型推断
  5. ✅ 开发体验:提供更好的开发工具支持

在 一些项目中,withDefaults定义组件 props 的标准方式,结合了 TypeScript 的类型安全和 Vue 的运行时特性。