本文 AI 產出,尚未審核

Vue3 元件基礎:Props 驗證(type、required、default)

簡介

在 Vue3 中,Props 是父層元件向子層元件傳遞資料的主要管道。若沒有適當的驗證機制,錯誤的資料類型或遺漏的必填欄位會導致元件在執行時拋出錯誤、渲染異常,甚至影響整體使用者體驗。

Vue3 為每個 Prop 提供了 type、required、default 三大屬性,讓開發者能在編譯階段就捕捉到不符合預期的資料。掌握這些驗證手法,不僅能提升程式的可讀性與維護性,也能在大型專案中避免因資料不一致而產生的難以排除的 bug。

本篇文章將從概念說明、實作範例、常見陷阱與最佳實踐,逐步帶你了解如何在 Vue3 中正確地使用 Props 驗證,讓你的元件更安全、更易於重用。

核心概念

1. type:限制傳入資料的類型

type 用於指定 Prop 必須是什麼資料型別。Vue 內建支援的類型包括 StringNumberBooleanArrayObjectFunctionSymbol,以及自訂的建構子(例如自訂類別)。

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 允許你為其指定一個備援值。對於 非原始類型(如 ObjectArray)的預設值,必須使用函式回傳,以避免所有實例共享同一個引用。

export default {
  props: {
    // 原始類型的預設值可以直接寫值
    isActive: {
      type: Boolean,
      default: false
    },
    // 陣列預設值必須是函式
    tags: {
      type: Array,
      default: () => []   // 每個元件實例得到獨立的陣列
    },
    // 物件預設值同理
    config: {
      type: Object,
      default: () => ({
        theme: 'light',
        size: 'medium'
      })
    }
  }
}

4. 完整寫法:結合 typerequireddefault

在實務開發中,通常會同時使用三個屬性來描述一個 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 為關鍵資訊,必須由父層提供;agecity 有合理的預設值,減少父層必傳的負擔。


範例 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>

說明:除了 typerequireddefault,Vue 允許使用 validator 函式自訂更細緻的規則。本例確保 value 必須是 1~5 的整數,即使傳入字串也會自動轉換檢查。


常見陷阱與最佳實踐

陷阱 可能的後果 解決方案 / 最佳實踐
忘記為非原始類型使用函式回傳 default 所有元件實例共享同一個陣列或物件,導致資料互相污染。 永遠使用 default: () => ({})default: () => []
只使用 type 而未加 required 父層忘記傳入重要資料時,元件會在渲染時出現 undefined 錯誤。 針對關鍵欄位加上 required: true,並在開發階段檢查 console 警告。
使用 ObjectArray 作為 type,卻在 default 直接寫值 Vue 會在開發模式拋出警告,正式環境則可能產生不可預期的行為。 依照上表使用函式返回預設值。
validator 中拋出例外 效能下降且錯誤訊息不清晰。 僅回傳 true/false,必要時在 console.warn 中說明原因。
props 中直接修改傳入值 破壞單向資料流,導致父層狀態不同步。 永遠把 Props 視為唯讀,若需要改變,使用 emit 或本地 data 複製。

最佳實踐清單

  1. 先寫驗證,再寫元件邏輯:確保所有外部輸入在進入元件前都已符合預期。
  2. 使用 TypeScript 時搭配 defineProps:可在編譯階段即捕捉類型錯誤,減少 runtime 警告。
  3. 為所有非必填的 Props 提供合理的 default,避免在模板中出現 undefined
  4. 將驗證規則集中管理(例如建立 propTypes.js),提升可維護性。
  5. 在單元測試中加入 Props 驗證測試,確保未來變更不會破壞介面契約。

實際應用場景

1. 表單元件(Input、Select、Checkbox)

在表單元件中,valueoptionsdisabled 等屬性往往必須有明確類型與預設值。例如:

props: {
  modelValue: { type: [String, Number], required: true },
  options: { type: Array, default: () => [] },
  disabled: { type: Boolean, default: false }
}

這樣的設定可以保證即使父層忘記傳遞 options,元件仍能正常渲染空選單,不會因 undefined 而拋錯。

2. 圖表或資料視覺化元件

圖表元件常需要大量設定(如 chartDatachartOptions),若不使用 defaultvalidator,在資料未完整時會導致圖表渲染失敗。

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 元件開發的基礎防線,透過 typerequireddefault(以及 validator)可以在開發階段即捕捉錯誤,提升元件的穩定性。
  • 非原始類型的預設值必須使用函式回傳,避免多實例共享同一個引用。
  • 必填欄位與合理的預設值 能減少父層傳遞資料的負擔,同時避免 undefined 帶來的渲染問題。
  • 結合 TypeScript、單元測試與集中管理,可以讓大型專案的 Props 合約更清晰、維護更容易。

掌握這些核心概念與實務技巧,你就能在 Vue3 專案中寫出 安全、可預測、易於重用 的元件,為前端開發奠定堅實的基礎。祝你開發愉快!