父子元件的資料傳遞

簡介
- 在 Vue 當中,常常會遇到要從外部元件傳遞資料到內部元件
- 例如一些客製化表單元件,如果我們想重複使用,必須封裝成 UI 元件,再從外部傳入資料
- 以下整理幾種常用寫法提供我自己參考
方法 1:使用 props 和 emit
一般寫法
- 在父元件中,傳遞
props
到MyInput
- 在子元件中,接收
props
,顯示在畫面上 - 建立
emit
,當 Input 內的值變動時,把更新的資料傳出到父元件
// 父元件<script setup lang="ts">import MyInput from '@/components/MyInput.vue'const msg = ref('')const updateMsg = (newMsg: string) =. { msg.value = newMsg}</script><template> <MyInput :input-value="msg" @update-value="updateMsg" /></template>
// MyInput.vue<script setup lang="ts">import { defineProps, defineEmits } from 'vue'defineProps<{ inputValue: String}>()const emit = defineEmits(['update-value'])const onInput = (event: Event) => { const value = (event.target as HTMLInputElement).value emit('update-value', value)}</script><template> <input type="text" :value="inputValue" @input="onInput" /></template>
Vue3 寫法
- 使用
modelValue
,update:modelValue
// 父元件<script setup lang="ts">import MyInput from '@/components/MyInput.vue'const msg = ref('')</script><template> <MyInput :modelValue="msg" @update:modelValue="msg = $event" /></template>
- 自訂
props
的名稱,替代modelValue
<MyInput :modelValue:customMsg="msg" @update:modelValue="msg = $event" />
// MyInput.vue<script setup lang="ts">import { defineProps, defineEmits } from 'vue'defineProps<{ modelValue: String}>()const emit = defineEmits(['update:modelValue'])</script><template> <input type="text" :value="modelValue" @input="emit('update:modelValue', ($event.target as HTMLInputElement).value)" /></template>
使用 v-model
- 可以在父元件,直接使用 v-model 簡化程式碼
MyInput
則維持不變
// 父元件<script setup lang="ts">import MyInput from '@/components/MyInput.vue'const msg = ref('')</script><template> <MyInput v-model="msg" /></template>
// MyInput.vue<script setup lang="ts">import { defineProps, defineEmits } from 'vue'defineProps<{ modelValue: String}>()const emit = defineEmits(['update:modelValue'])</script><template> <input type="text" :value="modelValue" @input="emit('update:modelValue', ($event.target as HTMLInputElement).value)" /></template>
方法 2: Writable computed
- 在
MyInput
內新增 computed 變數myMsg
- 設定這個變數的
getter
和setter
- 和 Input 標籤使用 v-model 雙向綁定
- 當 Input 內的值變化時,
myMsg
的值跟著改變,觸發setter
- 當
setter
觸發時,執行emit
,將更新的值傳出到父元件
// 父元件<script setup lang="ts">import MyInput from '@/components/MyInput.vue'const msg = ref('')</script><template> <MyInput v-model="msg" /></template>
// MyInput.vue<script setup lang="ts">import { defineProps, defineEmits, computed } from 'vue'const props = defineProps<{ modelValue: String}>()const emit = defineEmits(['update:modelValue'])const myMsg = computed({ get() { return props.modelValue }, set(newVal) { emit('update:modelValue', newVal) }})</script><template> <input type="text" v-model="myMsg" /></template>
方法 3:使用 VueUse 的 useVModel
- 直接引入
useVModel
- 不用自己寫
computed
的getter
和setter
// MyInput.vue<script lang="ts" setup>import { useVModel } from '@vueuse/core'const props = defineProps<{ modelValue: string}>()const emit = defineEmits(['update:modelValue'])const myMsg = useVModel(props, 'modelValue', emit)</script><template> <input type="text" v-model="myMsg" /></template>
方法 4:使用 defineModel
- 在 Vue 3.4 中,新加入的 API(Stable)
- 簡化了以上這些寫法
範例:傳入一個字串
// 父元件<script setup lang="ts">import MyInput from '@/components/MyInput.vue'const msg = ref('Hello world')</script><template> <MyInput v-model="msg" /></template>
// MyInput.vue<script setup lang="ts">const model = defineModel()</script><template> <input v-model="model" /></template>
範例:傳入一個物件
// 父元件<script setup lang="ts">import MyInput from '@/components/MyInput.vue'const searchData = ref({ keyword: 'Hello', page: 1})</script><template> <MyInput v-model="searchData" /></template>
// MyInput.vue<script setup lang="ts">interface SearchData { keyword: string page: number}const model = defineModel<SearchData>()</script><template> <input v-model="model.keyword" /> {{ model.page }}</template>
參考資料
# Vue # V-model # Computed