【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的类型和默认值配置。
相关推荐
全马必破三9 分钟前
浏览器原理知识点总结
前端·浏览器
零Suger11 分钟前
React 组件通信
前端·react.js·前端框架
LYFlied14 分钟前
【每日算法】 LeetCode 394. 字符串解码
前端·数据结构·算法·leetcode·面试·职场和发展
前端不太难21 分钟前
RN Navigation vs Vue Router 的架构对比
javascript·vue.js·架构
硕子鸽24 分钟前
UniApp + Dify 实战:详解 SSE 流式响应的解析与前端渲染
前端·uni-app·dify
lxh011325 分钟前
复原IP地址
前端·数据结构·算法
Miketutu28 分钟前
[特殊字符] uni-app App 端实现文件上传功能(基于 xe-upload 插件)
前端·vue.js·uni-app
焚 城30 分钟前
uniapp 各种文件预览实现
vue.js·uni-app·html
San30.34 分钟前
现代前端工程化实战:从 Vite 到 Vue Router 的构建之旅
前端·javascript·vue.js