VUE元件--props資料捕手

在談 props 之前,先來說說子元件的資料來源:

  • 自己的資料:也就是子元件裡面自定義的資料。
  • 父元件 props 進子元件的資料:這是子元件跟父元件借資料來用,不屬於子元件所有。
  • Store 裡面大家公用的資料: 例如放在 pinia 與 vuex 裡面的資料。

如果是要同層級的元件間互相傳遞資料,或是跨好幾個層級的元件要傳遞資料,並不適合使用 props。

props 是子元件的資料接收器

父元件如果要傳送資料給子元件必須透過 props ,而且必須遵守單向資料流的規則 ,也就是子元件接收到父元件的資料後,不能去修改那個資料,因為那份資料可能是給很多個子元件共用的。

內層子元件定義 props 接收

<script setup> 來舉例,必須在子元件先定義 defineProps

<script setup>
    defineProps(['title','message'])
</script>
<template>
    {{title}}
</template>    

或是用一個變數來定義 defineProps

<script setup>
    const prop=defineProps(['title','message'])
    console.log(props.title)
</script>
<template>
    {{props.title}}
</template>

可以在陣列中放入多個接收父元件資料的 props 聲明,Vue 才能知道父元件傳入子元件的哪些是 props。而defineProps 預設會返回一個帶有所 props 屬性的物件。

一個元件可以有多個 props,預設都可以接收任何型別的值,包含字串、數值、布林值、陣列、物件及函數。

子元件內的 props 命名要使用小駝峰形式。

defineProps(['ironMan'])

父元件中用 v-bind 綁定props,則使用 kebab-case

<MyComponent iron-man="tony" />

props 接收動態資料

而在父層中要傳送資料給子元件,可以這樣做:

<template>
    <ChildComp :message="catcher"></ChildComp>
</tmplate>

在上面的範例中,使用 v-bind 綁定內層子元件的 props,後面則是放外層父元件要傳送的資料,口訣為「前內後外」。

假設子元件的內層 defineProps 如此聲明:

<script setup>
import { ref } from 'vue';

defineProps(['msg']);

const count = ref(0);
function addNum(n) {
  count.value += n;
}
</script>

<template>
  <h2>{{ msg }}</h2>
  <button @click="addNum(1)">ADD</button>
  Count: {{ count }}
</template>

父元件的資料如此定義:

<script setup>
import { ref } from 'vue';
import HelloWorld from './components/HelloWorld.vue';

const name = ref('第二個HelloWorld');

</script>

外層父元件可以如此傳資料給子元件:

<HelloWorld :msg="name" />
// msg 是內層的 props,name 是外層的變數    

props 接收靜態資料

如果是靜態資料,依樣是遵守「前內後外」的原則,但是不用 v-bind 綁定。

<ChildComp :message="我是一個要傳入子元件的靜態資料"></ChildComp>

外層父元件可以如此傳資料給子元件:

<HelloWorld :msg="第一個HelloWorld" />
// msg 是內層的 props,name 是靜態的字串    

示範範例

props 與 v-for 的結合

如果父元件要傳給子元件的資料是陣列的形式,而且每個子元件要呈現的資料隨陣列的 index 不同,可以搭配 v-for 來傳遞多筆資料。

父元件中的資料型態:

const msg = ref([
    {id: 1,text: '我是第一個元件'},
    {id: 2,text: '我是第二個元件'},
    {id: 2,text: '我是第三個元件'},    
])

在子元件中定義 defineProps

<script setup>
    defineProps(['message'])
</script>
<template>
    {{message}}
</template>

在父元件中使用 v-for 渲染多個子元件

<ChildComp v-for="item in msg" :message="item.text" :key="item.id"></ChildComp>

換言之,假設要在父元件中放入多個子元件,而且子元件分別要呈現不同資料,則資料可以設計成陣列物件的形式,用 v-for 來跑子元件。

示範範例

單向數據流

前面有提到 props 由父元件往子元件傳遞資料是單向數據流,當父元件的資料更新的時候,所有子元件的 props 會更新到最新值,所以如果你在某個元件更改了 props 的資料,則會破壞子元件間的獨立狀態,Vue 會在控制台中示警。

這意味著你不能也不該在子元件中更改 props 的資料。

但是如果你要在子元件中操作運算 props 進來的資料,可以這樣做:

props 作為初始值,指定給一個新的變數

傳入的 props 要變成某個物件的屬性,再指給一個新的變數,這樣新的變數就可以自由操作運算,而不會去改到父層的資料。

<script setup>
import { ref } from 'vue';
const prop = defineProps(['msg']);
// 要把 msg 變成一個物件的屬性,才能運作
const newMsg = ref(prop.msg);
</script>

<template>
  <h2>msg {{ msg }}</h2>
  <h3>newMsg {{ newMsg }}</h3>
  <input v-model="newMsg" />
</template>

示範範例

將傳入的 props 轉換,例如把他指定給一個 computed 去產生一個新的資料。

props 驗證

前面範例的 props 大多放在一個陣列之中,但是實務上,放入物件是比較好的做法,這樣做的同時可以把驗證的條件帶入 props 的屬性中,例如指定型別、設定預設值及是否為必須。

以下說明,取自 Vue 的官網:

defineProps({
  // 基礎型別檢查
  // (帶入 `null` 和 `undefined` 會跳過任何型別檢查)
  propA: Number,
  // 檢查多種型別
  propB: [String, Number],
  // require 必傳,且為 String 型別
  propC: {
    type: String,
    required: true
  },
  // Number 型別,default 為預設值
  propD: {
    type: Number,
    default: 100
  },
  // 物件型別的預設值
  propE: {
    type: Object,
    // 物件或陣列的預設值
    // 必须从一个工厂函数返回。
    // 该函数接收组件所接收到的原始 prop 作为参数。
    default(rawProps) {
      return { message: 'hello' }
    }
  },
  // 自訂義行型別叫驗函數
  propF: {
    validator(value) {
      // The value must match one of these strings
      return ['success', 'warning', 'danger'].includes(value)
    }
  },
  // 函數型別的預設值
  propG: {
    type: Function,
    // 不像对象或数组的默认,这不是一个
    // 工厂函数。这会是一个用来作为默认值的函数
    default() {
      return 'Default function'
    }
  }
})

VUE元件--props資料捕手
https://popeye-ux.github.io/2023/06/30/vue-component-props/
作者
POPEYE
發布於
2023年6月30日
許可協議