Vue3 元件基礎:Props 驗證(type、required、default)
簡介
在 Vue3 中,Props 是父層元件向子層元件傳遞資料的主要管道。若沒有適當的驗證機制,錯誤的資料類型或遺漏的必填欄位會導致元件在執行時拋出錯誤、渲染異常,甚至影響整體使用者體驗。
Vue3 為每個 Prop 提供了 type、required、default 三大屬性,讓開發者能在編譯階段就捕捉到不符合預期的資料。掌握這些驗證手法,不僅能提升程式的可讀性與維護性,也能在大型專案中避免因資料不一致而產生的難以排除的 bug。
本篇文章將從概念說明、實作範例、常見陷阱與最佳實踐,逐步帶你了解如何在 Vue3 中正確地使用 Props 驗證,讓你的元件更安全、更易於重用。
核心概念
1. type:限制傳入資料的類型
type 用於指定 Prop 必須是什麼資料型別。Vue 內建支援的類型包括 String、Number、Boolean、Array、Object、Function、Symbol,以及自訂的建構子(例如自訂類別)。
export default {
props: {
// 只能接受字串
title: String,
// 只能接受數字或布林值(使用陣列列舉多種類型)
count: [Number, Boolean],
// 只能接受自訂物件(例如 Date)
deadline: Date
}
}
Tip:若傳入的值與
type不符,Vue 會在開發環境的 console 中顯示警告,協助你快速定位問題。
2. required:標記必填的 Prop
有些資料在元件運作時是不可或缺的,這時可以使用 required: true 強制父層必須提供該屬性。
export default {
props: {
// 必填,若未提供會在 console 警告
userId: {
type: Number,
required: true
},
// 非必填
nickname: String
}
}
注意:
required只在 開發模式 產生警告,正式環境(production)不會執行驗證,請務必在測試階段確認所有必填欄位都有傳入。
3. default:提供預設值
當父層未傳入某個 Prop 時,default 允許你為其指定一個備援值。對於 非原始類型(如 Object、Array)的預設值,必須使用函式回傳,以避免所有實例共享同一個引用。
export default {
props: {
// 原始類型的預設值可以直接寫值
isActive: {
type: Boolean,
default: false
},
// 陣列預設值必須是函式
tags: {
type: Array,
default: () => [] // 每個元件實例得到獨立的陣列
},
// 物件預設值同理
config: {
type: Object,
default: () => ({
theme: 'light',
size: 'medium'
})
}
}
}
4. 完整寫法:結合 type、required、default
在實務開發中,通常會同時使用三個屬性來描述一個 Prop 的完整需求。
export default {
props: {
/** 商品名稱:必填、字串 */
productName: {
type: String,
required: true
},
/** 庫存數量:非必填、數字、預設 0 */
stock: {
type: Number,
default: 0
},
/** 折扣比例:非必填、數字或字串、預設 0% */
discount: {
type: [Number, String],
default: '0%'
},
/** 商品屬性設定:非必填、物件、預設空物件 */
options: {
type: Object,
default: () => ({})
}
}
}
程式碼範例
下面提供 5 個實用範例,展示如何在不同情境下運用 Props 驗證。
範例 1:簡易訊息卡片(只驗證 type)
// MessageCard.vue
<template>
<div class="card">
<h3>{{ title }}</h3>
<p>{{ content }}</p>
</div>
</template>
<script>
export default {
name: 'MessageCard',
props: {
title: String, // 只要是字串即可
content: String,
/** 允許傳入布林值或字串,決定是否顯示底色 */
highlighted: [Boolean, String]
}
}
</script>
說明:此卡片元件只需要確保傳入的值是字串或布林,若類型錯誤,開發者會在 console 看到警告,避免 UI 異常。
範例 2:必填欄位與預設值結合
// UserProfile.vue
<template>
<section>
<h2>{{ name }}</h2>
<p>年齡:{{ age }}</p>
<p>城市:{{ city }}</p>
</section>
</template>
<script>
export default {
props: {
// 必填,若未提供會警告
name: {
type: String,
required: true
},
// 非必填,預設 18 歲
age: {
type: Number,
default: 18
},
// 非必填,預設 '台北'
city: {
type: String,
default: '台北'
}
}
}
</script>
說明:name 為關鍵資訊,必須由父層提供;age 與 city 有合理的預設值,減少父層必傳的負擔。
範例 3:陣列與物件的預設值(使用函式)
// TagList.vue
<template>
<ul>
<li v-for="tag in tags" :key="tag">{{ tag }}</li>
</ul>
</template>
<script>
export default {
props: {
// 若未傳入 tags,預設為空陣列
tags: {
type: Array,
default: () => [] // 每個實例得到獨立陣列
},
// 設定樣式的物件,預設主題為 light
styleConfig: {
type: Object,
default: () => ({
theme: 'light',
fontSize: '14px'
})
}
}
}
</script>
說明:使用函式返回的陣列/物件可以避免多個元件實例共享同一個引用,從而防止意外的資料互相污染。
範例 4:自訂類別作為 Prop(進階型別驗證)
// DatePicker.vue
<template>
<input type="date" :value="modelValue" @input="onInput" />
</template>
<script>
export default {
props: {
// 必須是 Date 物件
modelValue: {
type: Date,
required: true
}
},
methods: {
onInput(event) {
const newDate = new Date(event.target.value)
this.$emit('update:modelValue', newDate)
}
}
}
</script>
說明:Date 為內建建構子,Vue 會檢查傳入的值是否為 Date 實例。若父層傳入字串,開發者會立即收到警告,提醒需要先轉換為 Date。
範例 5:多型別結合 validator 自訂驗證
// RatingStars.vue
<template>
<div class="rating">
<span v-for="n in max" :key="n" :class="{ active: n <= value }">★</span>
</div>
</template>
<script>
export default {
props: {
// 支援 Number 或字串('5'),且必須在 1~5 之間
value: {
type: [Number, String],
required: true,
validator: (val) => {
const num = Number(val)
return Number.isInteger(num) && num >= 1 && num <= 5
}
},
// 最大星等,預設 5
max: {
type: Number,
default: 5
}
}
}
</script>
說明:除了 type、required、default,Vue 允許使用 validator 函式自訂更細緻的規則。本例確保 value 必須是 1~5 的整數,即使傳入字串也會自動轉換檢查。
常見陷阱與最佳實踐
| 陷阱 | 可能的後果 | 解決方案 / 最佳實踐 |
|---|---|---|
忘記為非原始類型使用函式回傳 default |
所有元件實例共享同一個陣列或物件,導致資料互相污染。 | 永遠使用 default: () => ({}) 或 default: () => []。 |
只使用 type 而未加 required |
父層忘記傳入重要資料時,元件會在渲染時出現 undefined 錯誤。 |
針對關鍵欄位加上 required: true,並在開發階段檢查 console 警告。 |
使用 Object、Array 作為 type,卻在 default 直接寫值 |
Vue 會在開發模式拋出警告,正式環境則可能產生不可預期的行為。 | 依照上表使用函式返回預設值。 |
validator 中拋出例外 |
效能下降且錯誤訊息不清晰。 | 僅回傳 true/false,必要時在 console.warn 中說明原因。 |
在 props 中直接修改傳入值 |
破壞單向資料流,導致父層狀態不同步。 | 永遠把 Props 視為唯讀,若需要改變,使用 emit 或本地 data 複製。 |
最佳實踐清單
- 先寫驗證,再寫元件邏輯:確保所有外部輸入在進入元件前都已符合預期。
- 使用 TypeScript 時搭配
defineProps:可在編譯階段即捕捉類型錯誤,減少 runtime 警告。 - 為所有非必填的 Props 提供合理的
default,避免在模板中出現undefined。 - 將驗證規則集中管理(例如建立
propTypes.js),提升可維護性。 - 在單元測試中加入 Props 驗證測試,確保未來變更不會破壞介面契約。
實際應用場景
1. 表單元件(Input、Select、Checkbox)
在表單元件中,value、options、disabled 等屬性往往必須有明確類型與預設值。例如:
props: {
modelValue: { type: [String, Number], required: true },
options: { type: Array, default: () => [] },
disabled: { type: Boolean, default: false }
}
這樣的設定可以保證即使父層忘記傳遞 options,元件仍能正常渲染空選單,不會因 undefined 而拋錯。
2. 圖表或資料視覺化元件
圖表元件常需要大量設定(如 chartData、chartOptions),若不使用 default 與 validator,在資料未完整時會導致圖表渲染失敗。
props: {
chartData: {
type: Object,
required: true,
validator: (val) => Array.isArray(val.series) && Array.isArray(val.labels)
},
chartOptions: {
type: Object,
default: () => ({
responsive: true,
maintainAspectRatio: false
})
}
}
3. 多語系(i18n)元件
語系文字往往以物件形式提供,若直接使用 default: {},所有元件會共享同一個物件,導致切換語系時互相干擾。
props: {
locale: { type: String, default: 'zh-TW' },
messages: {
type: Object,
default: () => ({
'zh-TW': { welcome: '歡迎' },
en: { welcome: 'Welcome' }
})
}
}
4. 動態載入的子元件
在使用 defineAsyncComponent 時,子元件的 props 仍需要完整驗證,否則在異步載入完成前的佔位元件可能會因缺少資料而崩潰。
總結
- Props 驗證是 Vue3 元件開發的基礎防線,透過
type、required、default(以及validator)可以在開發階段即捕捉錯誤,提升元件的穩定性。 - 非原始類型的預設值必須使用函式回傳,避免多實例共享同一個引用。
- 必填欄位與合理的預設值 能減少父層傳遞資料的負擔,同時避免
undefined帶來的渲染問題。 - 結合 TypeScript、單元測試與集中管理,可以讓大型專案的 Props 合約更清晰、維護更容易。
掌握這些核心概念與實務技巧,你就能在 Vue3 專案中寫出 安全、可預測、易於重用 的元件,為前端開發奠定堅實的基礎。祝你開發愉快!