以自訂元件 CustomInput
舉例
<script setup> const txt = ref(''); </script> <template> <CustomInput v-model="txt" /> </template>
v-model
會被展開為如下的形式
<CustomInput :modelValue="txt" @update:modelValue="newValue => txt = newValue" />
af7e595dcdbda7820db674c9e899f5bc
元件內部需要做兩件事:
d5fd7aea971a85678ba271703566ebfd 元素的
value attribute 綁定到
modelValue prop
input
1111167703好的 ##事件觸發時,觸發一個攜帶了新值的
update:modelValue
這裡是對應的程式碼:
<script setup> const props = defineProps({ 'modelValue': String, }) const emit = defineEmits(["update:modelValue"]) </script> <template> <input :value="modelValue" @input="$emit('update:modelValue', $event.target.value)" /> </template>
有些人會覺得這種寫法過於繁瑣,會導致標籤程式碼變得冗長
另一種在元件內實作
v-model 的方式是使用一個可寫的,同時具有getter 和setter 的
computed 屬性computed 綁定使用
computed
modelValue prop,而
set 方法需要觸發對應的事件
<script setup> const value = computed({ get() { return props.modelValue }, set(value) { emit("update:modelValue", value) } }) </script> <template> <input v-model="value" /> </template>
這個寫法可以簡化標籤中的屬性,邏輯清晰
單一屬性可以使用v-model 輕鬆搞定,如果多個屬性都需要雙向綁定呢?
預設情況下,
v- model 在元件上都是使用
modelValue 作為prop,並以
update:modelValue
v -model
指定一個參數來改變這些名字:<template> <CustomInput v-model:first-name="first" v-model:last-name="last" /> </template>同樣的,也可以用兩種方式綁定,只是
prop 從原來的
modelValue
update:參數名稱
<script setup> const props = defineProps({ firstName: String, lastName: String, }) // 在computed中 使用 const emit = defineEmits(['update:firstName', 'update:lastName']) </script> <template> <input type="text" :value="firstName" @input="$emit('update:firstName', $event.target.value)" /> <input type="text" :value="lastName" @input="$emit('update:lastName', $event.target.value)" /> </template>
綁定物件
在一個複雜的元件中,如果多個欄位需要雙向綁定,如果使用上文所示方法的話,會有一些繁瑣
介紹兩種雙向綁定物件的做法modelValue定義父元件
searchBar為一個複雜表單元件
<script setup> import { ref } from "vue" const modelValue = ref({ keyword: "123", selectValue: "", options: [ { label: "全部", value: "" }, { label: "a1", value: "1" }, { label: "a2", value: "2" }, ] }) </script> <template> <searchBar v-model="modelValue" /> </template>那麼在 searchBar 元件內,我們接收
並定義類型為 #Object
<template> <div> <!-- <input type="text" v-model="modelValue.keyword"> 可以实现双向绑定 --> <input type="text" :value="modelValue.keyword" @input="handleKeywordChange" > <select v-model="modelValue.selectValue"> <option v-for="o in modelValue.options" :key="o.value" :value="o.value"> {{ o.label }} </option> </select> </div> </template> <script lang="ts" setup> const props = defineProps({ modelValue: { type: Object, default: () => ({}) } }) const emit = defineEmits(["update:modelValue"]); // 以 input 举例 const handleKeywordChange=(val)=>{ emit("update:modelValue",{ ...props.modelValue, keyword:val.target.value }) } </script>
如果傳入物件的話,如註解所介紹的d78f68ab62ee45d15a833ea4e4e1b0c7雖然可以直接進行雙向綁定,但是這樣會破壞
emit 觸發事件一樣,但是傳遞的資料則變成了物件
雖然使用emit 可以觸發雙向綁定,但是過於繁瑣,下面介紹一種更優雅的寫法,可以說是一種奇技淫巧--
computed 綁定,你可能會寫出這種程式碼
<template> <input type="text" v-model="model.keyword"> </template> <script lang="ts" setup> const model = computed({ get() { return props.modelValue }, set(value) { // console.log(value) // 发现没有打印 emit("update:modelValue", { ...props.modelValue, keyword: value }) } }) <script>但是當你輸入的時候,你會發現並沒有觸發
setter, 因為
computed 會做一層代理,代理物件沒有發生修改如果想要觸發
setter ,如下圖:
// 只有这样才会变化 model.value = { keyword:"asdfad" }
這種方法無法觸發
setter,也就無法雙向綁定,該怎麼辦呢? 在 getter 中傳回 一個
代理物件! 在
getter
代理物件! 在
getter 中傳回 一個
代理物件!
proxy 包裹原物件
那麼
v-model 綁定的是代理之後的對象,如果代理對象屬性發生了改變,則會觸發代理對像中的
set 方法,此時我們可以觸發
emit
const model = computed({ get() { return new Proxy(props.modelValue, { set(obj, name, val) { emit("update:modelValue", { ...obj, [name]: val }) return true } }) }, set(value) { emit("update:modelValue", { ...props.modelValue, keyword: value }) } })
修飾符
我們知道 v-model 有一些內建的修飾符,例如
.trim#,
.number
.lazy。
在某些場景下,我們可能想要一個自訂元件的
v-model 支援自訂的修飾符。 我們來建立一個自訂的修飾符 capitalize
<CustomInput v-model.capitalize="txt" />###我們加入了###capitalize###修飾符,他會被自動傳入到 ###prop### 中的 ######modelModifiers#######中###
<script setup> const props = defineProps({ modelValue: String, modelModifiers: { default: () => ({}) } }) const emitValue = (e) => { let value = e.target.value; // 使用 修饰符 if (props.modelModifiers.capitalize) { value = value.charAt(0).toUpperCase() + value.slice(1) } emit('update:modelValue', value) } </script> <template> <input :value="modelValue" @input="emitValue" /> </template>
以上是vue3中的v-model怎麼使用的詳細內容。更多資訊請關注PHP中文網其他相關文章!