【Vue】defineProps直接和withDefaults设置默认值区别

一、先回顾:Vue2 中 "解构丢失默认值" 的真实痛点(附案例)

在 Vue2 的 Options API 中,props 的默认值通过default配置定义,但只要对this.$propsthis进行解构,就会同时丢失响应式默认值,这是 Vue2 响应式原理(基于 Object.defineProperty)的固有局限。

vue 复制代码
<!-- Vue2 组件:TestProps.vue -->
<template>
  <div>
    <!-- 期望显示默认值 "默认标题",实际显示 undefined -->
    <h1>{{ title }}</h1>
    <!-- 期望显示默认值 100,实际显示 undefined -->
    <p>计数:{{ count }}</p>
  </div>
</template>

<script>
export default {
  props: {
    title: {
      type: String,
      default: "默认标题" // 配置了默认值
    },
    count: {
      type: Number,
      default: 100 // 配置了默认值
    }
  },
  mounted() {
    // 问题根源:对 this 解构,丢失响应式+默认值
    const { title, count } = this; 
    console.log(title); // undefined(而非 "默认标题")
    console.log(count); // undefined(而非 100)
  }
};
</script>
为什么会这样?
  1. Vue2 的this是 "响应式代理对象",this.title本质是通过this.$props.title的 getter 访问,自带 "读取默认值" 的逻辑;
  2. 一旦解构(const { title } = this),拿到的是 "当前时刻的原始值"(若父组件没传 props,此时值为undefined),既失去了对$props的关联,也无法触发默认值的读取逻辑;
  3. 即便直接解构this.$propsconst { title } = this.$props),结果也一样 ------ 因为$props的响应式依赖 "访问行为",解构会切断这种依赖。

Vue2 中要规避这个问题,只能不解构 ,全程用this.title访问,代码灵活性受限。

二、Vue3 不用 withDefaults:解构仍会丢默认值(附案例)

Vue3 的defineProps解决了部分响应式问题(返回的是 "响应式 Proxy 对象"),但如果直接对defineProps的结果解构,依然会丢失默认值 (响应式可通过toRefs保留,但默认值不行)

Vue3 无 withDefaults 的案例:解构后默认值 "失效"
vue 复制代码
<!-- Vue3 组件:TestProps.vue(无withDefaults) -->
<template>
  <div>
    <!-- 父组件没传props时,期望显示默认值,实际显示 undefined -->
    <h1>{{ title }}</h1>
    <p>计数:{{ count }}</p>
  </div>
</template>

<script setup lang="ts">
// 1. 定义props并设置默认值(通过第二个参数)
const props = defineProps<{
  title?: string;
  count?: number;
}>({
  title: {
    type: String,
    default: "默认标题"
  },
  count: {
    type: Number,
    default: 100
  }
});

// 2. 直接解构:丢失默认值(响应式可通过toRefs保留,但默认值不行)
const { title, count } = props; 
console.log(title); // undefined(而非 "默认标题")
console.log(count); // undefined(而非 100)

// 3. 用toRefs解构:保留响应式,但仍丢失默认值
import { toRefs } from "vue";
const { title: refTitle, count: refCount } = toRefs(props);
console.log(refTitle.value); // undefined(依然没有默认值)
</script>

可见:Vue3 中即便用了defineProps,若不配合withDefaults,Vue2 的 "解构丢失默认值" 问题依然存在 ------ 这正是withDefaults要解决的核心场景之一。

三、Vue3 withDefaults:同时解决 "解构丢默认值" 和 "类型安全"(附案例)

withDefaults是 Vue3 为defineProps设计的 "默认值增强工具",它的核心作用有两个:

  1. defineProps的默认值与 TypeScript 类型紧密绑定(类型安全);
  2. 确保解构后仍能读取到默认值(彻底解决 Vue2 遗留的痛点)。
Vue3 withDefaults 案例:解构后默认值 "生效"
vue 复制代码
<!-- Vue3 组件:TestProps.vue(用withDefaults) -->
<template>
  <div>
    <!-- 父组件没传props时,正确显示默认值 -->
    <h1>{{ title }}</h1> <!-- "默认标题" -->
    <p>计数:{{ count }}</p> <!-- 100 -->
  </div>
</template>

<script setup lang="ts">
// 1. 用withDefaults定义props:默认值与类型绑定
const props = withDefaults(defineProps<{
  title?: string; // 可选类型,配合默认值
  count?: number; // 可选类型,配合默认值
}>(), {
  // 定义默认值(会自动校验类型,不符合TS类型会报错)
  title: "默认标题", 
  count: () => 100, // 复杂类型(如对象/数组)需用函数返回(避免复用问题)
});

// 2. 直接解构:默认值生效,且响应式保留(因为withDefaults处理过)
const { title, count } = props; 
console.log(title); // "默认标题"(正确读取默认值)
console.log(count); // 100(正确读取默认值)

// 3. 父组件传值时,会覆盖默认值(符合预期)
// 若父组件调用:<TestProps title="新标题" count={200} />
// 此时解构后 title = "新标题",count = 200
</script>
withDefaults 为什么能解决问题?

withDefaults本质是对defineProps返回的 "响应式 props 对象" 做了一层 "增强代理":

  • 当访问props.title时,若父组件没传值,代理会自动返回withDefaults中定义的默认值;
  • 即便解构(const { title } = props),拿到的依然是 "带默认值逻辑的响应式引用"(而非原始值),所以默认值不会丢失;
  • 同时,withDefaults会强制默认值的类型与defineProps的 TS 类型一致(比如给title123,TS 会直接报错),比 Vue2 的type校验更严格。

四、总结:withDefaults 与 Vue2 遗留问题的关系

withDefaults`针对性解决了 Vue2 中 "解构丢失默认值" 的痛点,但它不是简单的 "补丁",而是 Vue3 结合 TypeScript 和组合式 API 的 "升级方案":

  1. 解决遗留痛点:彻底终结了 "解构必丢默认值" 的问题,让代码可以更灵活地使用解构;

  2. 超越 Vue2 的价值

    • 类型安全:默认值与 TS 类型强绑定,避免 "默认值类型与 props 类型不匹配" 的隐性 bug;
    • 复杂默认值友好:对对象 / 数组等复杂类型,强制用函数返回(避免 Vue2 中 "默认值复用导致的状态污染" 问题);
    • 与组合式 API 适配:在script setup中,让 props 的默认值定义更简洁,无需拆分defineProps的类型和默认值配置。
相关推荐
游九尘2 小时前
服务器都是用的iis, 前端部署后报跨域,不是用同一个服务器 是前端项目的服务器做Nginx转发,还是后端项目的服务器做Nginx转发?
服务器·前端·nginx
携欢2 小时前
PortSwigger靶场之DOM XSS in jQuery selector sink using a hashchange event通关秘籍
前端·jquery·xss
Apifox3 小时前
如何让 Apifox 发布的在线文档具备更好的调试体验?
前端·后端·测试
咔咔一顿操作3 小时前
【CSS 3D 交互】打造沉浸式 3D 照片墙:结合 JS 实现拖拽交互
前端·javascript·css·3d·交互·css3
0x0003 小时前
Uniapp - 自定义 Tabbar 实现
前端·uni-app
用户458203153173 小时前
Flexbox布局上手:10分钟告别垂直居中难题
前端·css
牛蛙点点申请出战3 小时前
仿微信语音 WaveView 实现
android·前端·ios
yiyesushu3 小时前
react + next.js + ethers v6 项目实例
前端
明远湖之鱼3 小时前
巧用 Puppeteer + Cheerio:批量生成高质量 Emoji 图片
前端·爬虫·node.js