2024.09.19
沒想過這篇文章能有這麼大的瀏覽量,身為作者的我也應該負起一些責任。所以各位讀者,如果你使用 vue 版本是 3.4 以上的,那麼恭喜你有更加優雅且簡潔的寫法,實現雙向綁定,只需要一個方法 defineModel()
Child Component
<!-- Child.vue -->
<script setup>
const model = defineModel()
function update() {
model.value++
}
</script>
<template>
<div>Parent bound v-model is: {{ model }}</div>
<button @click="update">Increment</button>
</template>
Parent Component
<!-- Parent.vue -->
<Child v-model="countModel" />
Input Component
<script setup>
const model = defineModel()
</script>
<template>
<input v-model="model" />
</template>
更多詳細的用法,可以到官方文檔去查閱。如果版本是 3.4 以下的讀者,建議還是升級,畢竟真的方便太多了。但礙於專案太大或是環境不允許,再請往下觀看,感謝大家的熱情點閱。
前言
在開發過程中,一但 template 太大,一個不順眼就想抽成一個 component 來管理。不論是在可讀性與狀態管理上都更加簡潔優雅。而在這個過程中難免會遇到需要在子層透過 v-model 綁定父層狀態的情況,解法很多種,目前覺得最快速的方式就是 provide 與 inject,但這樣寫的話如果要傳遞的參數一多,就會寫重複的樣板 code,版面也不是這麼好看,不論是父層與子層都會更複雜
直接綁定 props 的參數不就好了嗎?
我也曾經天真可愛,直接將 props 的參數傳進 component 裡面的 input 並透過 v-model 綁定,但系統會提示你這是個危險的行為。畢竟將父層的狀態綁定到子層的元件上,就會破壞了 Vue 注重的 One-Way Data Flow 原則。「所有的狀態或資料,都應該由擁有的元件自己來修改」,所以,在子元件內,Vue 不會讓開發者直接修改收到的 props
。
正確的起手式
開發者如果想在子元件中修改父元件的資料或狀態,最常見的作法,是在父元件進行監聽,等待子元件透過 emit
發送事件,告訴父元件更新資料或狀態。
總結就是自己實現 v-model ,將 props 與 emits 定義清楚
不過如果只是單純透過 props 與 emits ,還無法做到很漂亮的連動,這邊還需要借助 computed,就讓範例來為大家說明
子元件
可以看到在子元件中,我們是使用一個 computed 與 props 的參數連動,並且在 input 的 v-model 做到綁定,並非直接綁定 props 的參數。再透過 get 與 set 的效果,讓用戶修改 input 時,能馬上 emit 新的 value 給上層
<script setup>
import { computed } from "vue";
const props = defineProps({
value: String
});
const emits = defineEmits(["update:value"]);
const inputValue = computed({
get() {
return props.value;
},
set(newValue) {
emits("update:value", newValue);
}
});
</script>
<template>
<div class="container">
<input v-model="inputValue" />
</div>
</template>
父元件
在父層去傳送一個 ref 狀態物件來綁定剛剛自定義的元件,並透過 <pre> tag 來查看結果,是否能成功透過 v-model 來綁定子元件中的 input 元件
<script setup>
import { ref } from "vue";
import TestInput from "../components/presentation/TestInput.vue";
const data = ref("init data");
</script>
<template>
<div class="container">
<pre> {{ data }} </pre>
<test-input v-model:value="data" />
</div>
</template>
大功告成~!
小結
以上就是筆者我自己參考一些文章與官方文件摸索出來的做法,當然還有其他很多做法,像是上面提到的 provide 與 inject。但不論哪個做法,都應該去遵從單一資料流的原則,與保持狀態的響應性,避免在往後維護專案中的 components 時,搞不清楚之間狀態的層級與連結關係