Skip to content

Vue 3 實戰:掌握 Props 傳值技巧與 Data Validation 型別驗證

Vue 3 Props:defineProps 傳值與驗證

在 Vue 的 元件化架構 中,Props 是父元件向子元件傳遞資料的唯一官方管道。透過明確宣告 Props,Vue 才能精準區分哪些屬性是 資料輸入 ,哪些是單純的 屬性透傳

如何宣告 Props:使用 defineProps

<script setup> 中,我們使用內建函式 defineProps 來宣告元件接收的資料。

基礎陣列宣告

這是最簡單的方式,適合開發初期快速原型製作:

js
// defineProps(['name', 'age']);
const props = defineProps(['name', 'age']);

console.log(props.name);
console.log(props.age);

物件宣告與型別檢查

為了大型專案的維護性,建議使用 物件形式。這不僅能規範資料型別,當傳入錯誤型別時,Vue 還會在瀏覽器控制台發出警告,幫助您提早發現錯誤。

js
defineProps({
  name: {
    type: String,
    default: '',
  },
  age: {
    type: Number,
    default: 10,
  },
});

常用的驗證選項如下:

選項說明
type型別,可用 StringNumberBooleanArrayObjectFunction
default父層未傳入時的預設值;物件與陣列必須寫成工廠函式
required設為 true 時,父層未傳入會在主控台發出警告
validator自訂驗證函式,回傳 false 時發出警告
js
defineProps({
  // 物件、陣列的預設值必須用函式回傳,避免所有元件實體共用同一份資料
  skills: {
    type: Array,
    default: () => [],
  },
  size: {
    type: String,
    default: 'medium',
    validator: (value) => ['small', 'medium', 'large'].includes(value),
  },
});

靜態與動態 Props 的應用場景

在 Template 綁定 Props 時,需注意 靜態動態 的差異:

  • 靜態:值是固定字串,不會隨資料變動。

    html
    <!-- 傳入的是字串 "iPhone 17 Pro Max" -->
    <ProductCard title="iPhone 17 Pro Max" />
  • 動態:以 v-bind 綁定變數、表達式、運算結果。

    html
    <!-- 傳入的是變數 currentPrice 的值 -->
    <ProductCard :price="currentPrice" />

數字、布林、陣列一定要加冒號

沒有冒號時,傳進去的一律是 字串count="1" 得到的是字串 '1'disabled="false" 得到的是字串 'false'(在判斷式中會是 true)。要傳非字串的值,一律用 : 綁定。

命名慣例:camelCase 與 kebab-case

在 JavaScript 中宣告與存取用 camelCase,在模板上傳遞時用 kebab-case,Vue 會自動對應:

js
defineProps({ productName: String });
html
<ProductCard product-name="iPhone 17 Pro Max" />

重要規範:單向數據流 (One-Way Data Flow)

所有的 props 都遵循 單向數據 流的原則:

  • 數據向下流動:父層狀態更新時,Props 會自動同步給子元件。
  • 禁止逆向修改:子元件不應在內部直接修改 Props。這樣可以確保數據流向清晰,避免多個子元件同時修改父層狀態導致維護災難。

實戰提醒

如果您試圖在子元件內修改 Prop 值,Vue 會在主控台(Console)噴出警告。若需要修改該資料,請考慮使用 emit() 通知父層更新,或將其作為 ref() 的初始值轉化為元件內部狀態。

需要修改時的兩種正確作法

js
// 1. 只是想要一個以 prop 為初始值的內部狀態
const props = defineProps({ initialCount: Number });
const count = ref(props.initialCount);

// 2. 只是想加工後顯示,交給 computed
const formattedPrice = computed(() => `NT$ ${props.price.toLocaleString()}`);

真的需要更動父層資料時,請改用 emit 通知父層,或用 defineModel 建立雙向綁定。

實戰範例

靜態傳遞

動態傳遞

js
['html', 'rwd', 'javascript', 'vue', 'sass'];
css
.bevel {
  position: relative;
  padding: 1rem;
  color: #666;
  line-height: 1;
  list-style-type: none;
  text-transform: uppercase;
  border: solid 1px #ccc;
}

.bevel::before,
.bevel::after {
  position: absolute;
  width: 16px;
  height: 16px;
  background-color: #fff;
  content: "";
  transform: rotate(45deg);
}

.bevel::before {
  top: -9px;
  left: -9px;
  border-right: solid 1px #ccc;
}

.bevel::after {
  bottom: -9px;
  right: -9px;
  border-left: solid 1px #ccc;
}

.bevel + .bevel {
  margin-top: 1rem;
}

型別與驗證

js
{
  name: 'Away',
  fullName: () => `${user.name} Hung`,
  age: 41,
  isMale: true,
  skills: ['RWD', 'CSS', 'Javascript', 'HTML'],
  books: {
    name1: 'ACA 國際認證教戰手冊:Dreamweaver CS5 完全攻略',
    name2: 'Illustrator CS5(補習班內部用書)',
    name3: 'Illustrator CS4(補習班內部用書)',
  },
}

動態傳遞與狀態控管

js
[
  {
    id: 1,
    isRelease: false,
    name: '梵蒂岡驅魔士',
  },
  {
    id: 2,
    isRelease: true,
    name: '超級瑪利歐兄弟電影版',
  },
  {
    id: 3,
    isRelease: true,
    name: '捍衛任務4',
  },
  {
    id: 4,
    isRelease: false,
    name: '星際異攻隊3',
  },
  {
    id: 5,
    isRelease: true,
    name: '鈴芽之旅',
  },
];
css
/* 基本樣式 */
.box {
  padding: 0.7rem 1rem;
  margin-bottom: 1rem;
}

/* 已上映 */
.box--release {
  background-color: lightblue;
}

/* 未上映 */
.box--no-release {
  color: #ccc;
  background-color: #eee;
}

常見問題

陣列宣告與物件宣告該用哪一種?

陣列宣告只寫得出名稱,適合快速原型;正式專案建議使用物件宣告,可以指定型別、預設值與必填,型別不符時 Vue 會在主控台提出警告。

為什麼傳數字給元件要加冒號?

沒有冒號時傳進去的一律是字串,寫 count="1" 得到的是字串 '1'。要傳數字、布林、陣列或物件,必須用 v-bind 綁定,寫成 :count="1"

可以在子元件裡直接修改 props 嗎?

不行,這違反單向數據流,Vue 會發出警告。需要改動時,請用 emit 通知父層更新,或把 prop 當成初始值複製成元件自己的 ref

物件或陣列型別的 default 為什麼要寫成函式?

因為物件與陣列是參考型別,直接寫成字面值會讓所有使用該元件的實體共用同一份資料。必須改寫成工廠函式,例如:default: () => [],每個實體才會拿到各自的副本。

prop 名稱要用駝峰還是連字號?

在 JavaScript 中宣告與存取用 camelCase,在模板上傳遞時用 kebab-case,例如:宣告 productName、傳遞寫成 product-name。Vue 會自動對應這兩種寫法。

props 與 attrs 差在哪裡?

有在 defineProps 宣告的才是 props;沒宣告的屬性會變成 透傳屬性 attrs,預設自動掛到子元件的根元素上。

延伸閱讀