好心平安 發表於 2025-9-15 16:55:00

记录---用好了 defineProps 才叫会用 Vue3,90% 的写法都错了

<h1 data-id="heading-0">🧑‍💻 写在开头</h1>
<p>点赞 + 收藏 === 学会🤣🤣🤣</p>
<blockquote>
<p>Vue 3 的&nbsp;<code>Composition API</code>&nbsp;给开发者带来了更强的逻辑组织能力,但很多人用&nbsp;<code>defineProps</code>&nbsp;的方式,依然停留在 Vue 2 的“Options 语法心智”。本质上只是把&nbsp;<code>props: {}</code>&nbsp;拿出来“提前声明”,并没有真正理解它的运行机制、类型推导优势、默认值处理方式、解构陷阱等关键点。</p>
<p>这篇文章不做语法搬运,而是用实战视角,带你了解:defineProps 到底该怎么写,才是专业的 Vue3 写法。</p>
</blockquote>
<h2 data-id="heading-0">🎯 为什么说你用错了 defineProps?</h2>
<p>我们先来看一个常见的 Vue3 组件写法:</p>
<div class="cnblogs_Highlighter">
<pre class="brush:csharp;gutter:true;">&lt;script setup&gt;
const props = defineProps({
title: String,
count: Number
})
&lt;/script&gt;</pre>
</div>
<p>你以为这就完事了?它只是基本写法。但在真实业务中,我们往往会遇到:</p>
<ul>
<li>需要传默认值</li>
<li>想要类型推导</li>
<li>解构 props 却发现响应性丢失</li>
<li>TS 类型重复声明,不够优雅</li>
</ul>
<p>这些问题,defineProps 其实早就帮你解决了,只是你没用对方式。</p>
<hr>
<h2 data-id="heading-1">✅ 正确的三种 defineProps 写法</h2>
<h3 data-id="heading-2">① 写法一:声明式类型推导(推荐)</h3>
<div class="cnblogs_Highlighter">
<pre class="brush:csharp;gutter:true;">interface Props {
title: string
count?: number
}

const props = defineProps&lt;Props&gt;()</pre>
</div>
<p>优点:</p>
<ul>
<li>自动获得类型推导</li>
<li>在&nbsp;<code>&lt;script setup lang="ts"&gt;</code>&nbsp;中书写自然</li>
<li>可配合&nbsp;<code>withDefaults</code>&nbsp;补充默认值</li>
</ul>
<blockquote>
<p>这是 Composition API 的推荐写法,完全由 TypeScript 驱动,而不是运行时校验。</p>
</blockquote>
<hr>
<h3 data-id="heading-3">② 写法二:运行时代码校验(Options 式)</h3>
<div class="cnblogs_Highlighter">
<pre class="brush:csharp;gutter:true;">const props = defineProps({
title: {
    type: String,
    required: true
},
count: {
    type: Number,
    default: 0
}
})</pre>
</div>
<p>优点:</p>
<ul>
<li>保留 Vue2 的 props 校验逻辑</li>
<li>更适合 JS-only 项目(不使用 TS)</li>
</ul>
<p>缺点:</p>
<ul>
<li>类型推导不如泛型直观</li>
<li>与&nbsp;<code>withDefaults</code>&nbsp;不兼容</li>
</ul>
<hr>
<h3 data-id="heading-4">③ 写法三:与&nbsp;<code>withDefaults</code>&nbsp;配合(实战最常见)</h3>
<div class="cnblogs_Highlighter">
<pre class="brush:csharp;gutter:true;">const props = withDefaults(defineProps&lt;{
title?: string
count?: number
}&gt;(), {
title: '默认标题',
count: 1
})</pre>
</div>
<p>优势是:</p>
<ul>
<li>既能获得类型推导,又能写默认值</li>
<li>不会重复写 default</li>
<li>比纯 defineProps 更简洁易维护</li>
</ul>
<blockquote>
<p>注意:withDefaults 只能配合泛型式 defineProps 使用,不能和对象式 props 写法混用。</p>
</blockquote>
<hr>
<h2 data-id="heading-5">⚠️ 高发误区警告:你踩过几个?</h2>
<h3 data-id="heading-6">🚫 误区 1:直接解构 props,响应性丢失</h3>
<div class="cnblogs_Highlighter">
<pre class="brush:csharp;gutter:true;">const { title, count } = defineProps&lt;{ title: string, count: number }&gt;()</pre>
</div>
<p>上面的写法会让&nbsp;<code>title</code>&nbsp;和&nbsp;<code>count</code>&nbsp;成为普通变量,不是响应式的。</p>
<p>解决方式:使用&nbsp;<code>toRefs</code></p>
<div class="cnblogs_Highlighter">
<pre class="brush:csharp;gutter:true;">const props = defineProps&lt;{ title: string, count: number }&gt;()
const { title, count } = toRefs(props)</pre>
</div>
<p>这样才能在&nbsp;<code>watch(title, ...)</code>&nbsp;中有效监听变化。</p>
<hr>
<h3 data-id="heading-7">🚫 误区 2:类型和默认值重复声明</h3>
<div class="cnblogs_Highlighter">
<pre class="brush:csharp;gutter:true;">const props = defineProps({
title: {
    type: String as PropType&lt;string&gt;, // 写了类型
    default: 'Hello' // 又写默认值
}
})</pre>
</div>
<p>在 TS 项目中,这种方式显得繁琐且不智能。建议直接用泛型 + withDefaults,让 IDE 自动推导类型。</p>
<hr>
<h3 data-id="heading-8">🚫 误区 3:没有区分“开发期类型检查” vs “运行时校验”</h3>
<p>Vue3 的 Props 有两个模式:</p>
<ul>
<li>TypeScript 模式:靠 IDE + 编译器</li>
<li>Options 模式:在浏览器运行时报错</li>
</ul>
<p>实际推荐:生产环境靠 TypeScript 检查即可,无需运行时 Props 校验,提高性能。</p>
<hr>
<h2 data-id="heading-9">🎯 defineProps 是真正的组件契约声明</h2>
<p>在 Vue3 的&nbsp;<code>&lt;script setup&gt;</code>&nbsp;中,<code>defineProps</code>&nbsp;就是你和使用你组件的人之间的契约。</p>
<h3 data-id="heading-10">为什么说它是契约?</h3>
<ul>
<li>它声明了组件的“输入规范”</li>
<li>它决定了类型校验、默认值逻辑</li>
<li>它是组件文档的第一手来源</li>
</ul>
<p>你越是随便写它,越容易在团队协作时踩坑。</p>
<hr>
<h2 data-id="heading-11">💡 defineProps 的进阶技巧:你未必知道的几个点</h2>
<h3 data-id="heading-12">✔ 你可以在 defineProps 里使用类型别名</h3>
<div class="cnblogs_Highlighter">
<pre class="brush:csharp;gutter:true;">type Size = 'sm' | 'md' | 'lg'

withDefaults(defineProps&lt;{
size?: Size
}&gt;(), {
size: 'md'
})</pre>
</div>
<p>这是让&nbsp;<code>props.size</code>&nbsp;具备完整类型提示的关键方式。</p>
<hr>
<h3 data-id="heading-13">✔ 配合&nbsp;<code>defineEmits</code>&nbsp;写法更完整</h3>
<div class="cnblogs_Highlighter">
<pre class="brush:csharp;gutter:true;">const emit = defineEmits&lt;{
(e: 'submit', value: number): void
(e: 'cancel'): void
}&gt;()</pre>
</div>
<p>这样写出的组件,输入(props)+ 输出(emit)都具备契约,可以被任何 IDE 精确识别。</p>
<hr>
<h3 data-id="heading-14">✔ defineProps 写法决定你能不能使用 Volar 的类型推导</h3>
<p>很多人发现&nbsp;<code>&lt;MyComponent :title="xx" /&gt;</code>&nbsp;里没有类型提示,大概率是你组件没有正确写 defineProps 的泛型。保持结构清晰,是让 IDE 吃得饱的唯一方式。</p>
<hr>
<h2 data-id="heading-15">🚀 小结:defineProps 不只是 props,它是组件健壮性的开端</h2>
<p><img src="https://img2024.cnblogs.com/blog/2149129/202509/2149129-20250915165347935-68742973.png" alt="企业微信截图_20250915165242" loading="lazy"></p>
<p>正确思路是:在 TypeScript 项目中,尽可能采用&nbsp;<code>defineProps&lt;T&gt;() + withDefaults()</code>&nbsp;写法,做到类型明确、默认值清晰、响应式安全。</p>
<hr>
<h2 data-id="heading-16">📌 怎么判断你是否“真的会用 defineProps”?</h2>
<ul>
<li>❌ 你写了 defineProps 但 props 解构不响应</li>
<li>❌ 你写 default 写得很痛苦</li>
<li>❌ 你项目里 props 写法风格混乱</li>
<li>❌ 你的组件在 IDE 中没有 props 自动补全</li>
</ul>
<p>✅ 如果你能做到:</p>
<ul>
<li>使用泛型 +&nbsp;<code>withDefaults</code></li>
<li>保持 props 和 emits 的契约完整</li>
<li>清晰地类型提示和响应性解构</li>
</ul>
<p>那恭喜你,是真的理解了 Vue3 的组件心智模型。</p>
<div>
<h2>本文转载于:https://juejin.cn/post/7513117108114473001</h2>
</div>
<h3 id="tid-D8HBxE">如果对您有所帮助,欢迎您点个关注,我会定时更新技术文档,大家一起讨论学习,一起进步。</h3>
<p><em><img src="https://img2024.cnblogs.com/blog/2149129/202501/2149129-20250122165814748-630765389.png" alt="" loading="lazy"></em></p><br><br>
来源:https://www.cnblogs.com/smileZAZ/p/19093289
頁: [1]
查看完整版本: 记录---用好了 defineProps 才叫会用 Vue3,90% 的写法都错了