Vue3-06-props&emit 元件的溝通傳遞

Photo by Andrew Neel on Unsplash

Vue3-06-props&emit 元件的溝通傳遞

元件的溝通傳遞

Vue.js 每個元件的實體狀態、模板等作用範圍都應該要是獨立的, 也就是我們不能(也不應該)在子元件的模組「直接」去修改父元件,甚至是另一個元件的資料,因為元件化就是為了能夠重複使用,希望這些元件可以根據『外部&內部』資料的改變,來呈現出不同的結果,

  • 如果需要透過父層來傳遞資料給子層的話,就是 props
  • 如果需要透過子層來傳遞資料給父層的話,就是 emit

![props&emit 傳遞流程](d1dwq032kyr03c.cloudfront.net/upload/images.. "props&emit 傳遞流程")

Props(父層傳遞資料給子層)

![props 傳遞流程](book.vue.tw/assets/img/2-2-props-01.e62d8c8.. "props 傳遞流程")

實作範例為:將 App.vue 當作父層,製作元件 propsTest.vue 當作子層:

  • 父層:
    • 新增 msg 變數
    • 使用 v-bind 將 msg 傳入子層元件, :innerMsg(子層接收 msg 的變數名稱)="msg"
  • 子層:
    • 新增 props 設定,新增父層的自定義接收資料的變數 innerMsg
    • 使用 setup(props) 的方式,將 props return 出來
    • 在子層 template 內,透過 props.proxy 的方式將資料取出

parent-component:

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

// 新增 msg 變數
const msg = ref('')
</script>

<template>
    <div class="wrapper">
      <!-- propsTest component -->
      <!-- 使用 v-bind:子層接收資料的名稱=msg -->
      <PropsTest :innerMsg="msg" />
      <PropsTest />
    </div>
</template>

child-component:

<script>
export default {
    props: {
        innerMsg: {
            type: String,
            default: "Michael"
        }
    },
    setup(props) {
        return { props }
    }
}
</script>

<template>
    <!-- 從 props.proxy 去拿取內裡所需要的資料 -->
    <h1>{{ props.innerMsg }}</h1>
</template>

除錯處理

為了防止 props 傳入的資料型別與預期不同,以及遺漏傳入 props 資料的錯誤產生, 最好是用此方式來建立 props 設定,

  1. 新增 type 設定 確保傳入的型別有符合預期,如果傳入錯誤型別,瀏覽器會在 devtool 產生 warning 警告提示來讓我們知道,不會直接爆出錯誤
  2. 新增 default 設定 當沒有傳入變數時,則會顯示出 default 的數值,而不會爆出沒有傳入變數的錯誤
<script>
export default {
    props: {
        innerMsg: {
            type: String,
            default: "Michael"
        }
    },
    setup(props) {
        return { props }
    }
}
</script>

如果型別為 Array, Object, Function 寫法上會有些微差異,下方列出這三項的寫法差異:

<script>
export default {
    props: {
        innerMsg: {
            type: String,
            default: "沒有數值傳入",
        },
        arr: {
            type: Array,
            default: () => []
        },
        obj: {
            type: Object,
            // default: () => {
            //     return {
            //     }
            // },
            // 上方可以簡寫成下方這樣
            default: () => ({}),
        },
        func: {
            type: Function,
            default: () => { },
        }
    },
    setup(props) {
        return { props }
    },
}
</script>

emit(子層往父層傳遞)

Vue2 的方法是使用 this.$emit("emitTest","Michael"), 使用 this 的方式來將數值回傳給父層,但由於 composition API 是沒有 this 的,取而代之現在是使用 context 的方式來回傳 emit 數值給父層。

emit 有兩種使用方式:

  1. 傳入 context ,並且使用 context.emit 以及 return context

     setup(props, context) {
         const emitNum = ref(0)
    
         onMounted(() => {
             // console.log(context.emit);
             context.emit('Emit', emitNum.value)
         })
         return { emitNum, context }
     }
    
  2. 使用解構方式來使用 emit,寫起來會比範例 1 簡短許多

     setup(props, { emit }) {
         const emitNum = ref(0)
    
         onMounted(() => {
             // console.log(context.emit);
             emit('emitTest', emitNum.value)
         })
         return { emitNum }
     }
    

:::warning 而 props 如果沒有需要用到,是可以不用回傳出來的,但一定得放入 setup 裡頭,才有辦法使用 context 去使用 emit 回傳數值到父層。 :::

App.vue:

@子層定義的第一個參數名稱="父層方法"

<script setup>
import Emit from './components/Emit.vue';

const handleEmit = (e) => {
  console.log(e);
}
</script>

<template>
  <Emit @emitTest="handleEmit" />
</template>