組件 v-model
基本用法?
v-model 可以在組件上使用以實現雙向綁定。
從 Vue 3.4 開始,推薦的實現方式是使用 defineModel() 宏:
<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>
父組件可以用 v-model 綁定一個值:
<script setup lang="ts">
import { useAppStore } from '@/store/modules/app'
const appStore = useAppStore()
import modelChild from './components/test/modelChild.vue'
const textColor = computed(() => appStore.getTextColor)
appStore.initTheme()
const countModel = ref(10)
</script><template><ConfigGlobal><p :style="{'color':textColor}" >p標簽</p>{{ countModel }}<modelChild v-model="countModel"></modelChild></ConfigGlobal></template>
顯示如下:
defineModel() 返回的值是一個 ref。它可以像其他 ref 一樣被訪問以及修改,不過它能起到在父組件和當前變量之間的雙向綁定的作用:
- 它的 .value 和父組件的 v-model 的值同步;
- 當它被子組件變更了,會觸發父組件綁定的值一起更新。
這意味著你也可以用 v-model 把這個 ref 綁定到一個原生 input 元素上,在提供相同的 v-model 用法的同時輕松包裝原生 input 元素:
child.vue<script setup>
const model = defineModel()
</script><template><span>My input</span> <input v-model="model">
</template>
// app.vue
<script setup>
import Child from './Child.vue'
import { ref } from 'vue'const msg = ref('Hello World!')
</script><template><h1>{{ msg }}</h1><Child v-model="msg" />
</template>
底層機制
defineModel 是一個便利宏。編譯器將其展開為以下內容:
- 一個名為 modelValue 的 prop,本地 ref 的值與其同步;
- 一個名為 update:modelValue 的事件,當本地 ref 的值發生變更時觸發。
<!-- Child.vue -->
<script setup>
const props = defineProps(['modelValue'])
const emit = defineEmits(['update:modelValue'])
</script><template><input:value="modelValue"@input="emit('update:modelValue', $event.target.value)"/>
</template><!-- Parent.vue -->
<template><ConfigGlobal><p :style="{'color':textColor}" >p標簽</p>{{ foo }}<modelChild :modelValue="foo"@update:modelValue="$event => (foo = $event)"></modelChild></ConfigGlobal></template>
因為 defineModel 聲明了一個 prop,你可以通過給 defineModel 傳遞選項,來聲明底層 prop 的選項:
子組件
<script setup>
const model = defineModel({required:true, default: 1 }) // 對應 model 參數
</script><template><input v-model="model" type="text"><!-- <input v-model="age" type="number"> -->
</template>
父組件
<script setup lang="ts">
const countModel = ref()
console.log(countModel.value,'countModel'); // undefined
</script>
<template><ConfigGlobal>{{ countModel }}<modelChild v-model="countModel"></modelChild></ConfigGlobal></template>
如果為 defineModel prop 設置了一個 default 值且父組件沒有為該 prop 提供任何值,會導致父組件與子組件之間不同步。在下面的示例中,父組件的 countModel 是 undefined,而子組件的 model 是 1:
v-model 的參數
//子組件
<script setup>
const title = defineModel('title')
</script><template><input type="text" v-model="title" />
</template>// 父組件
<script setup>
import { ref } from 'vue'
import MyComponent from './MyComponent.vue'const bookTitle = ref('v-model argument example')
</script><template><h1>{{ bookTitle }}</h1><MyComponent v-model:title="bookTitle" />
</template>
不同屬性綁定多個 v-model
父組件?
<UserForm v-model:username="user.name"v-model:age="user.age"
/>
子組件
<script setup>
const username = defineModel('username') // 對應 username 參數
const age = defineModel('age') // 對應 age 參數
</script><template><input v-model="username" type="text"><input v-model="age" type="number">
</template>
處理 v-model 修飾符
父組件
使用內置修飾符(如 .trim):
<Child v-model.trim="text" />
子組件
<script setup>
const [model, modifiers] = defineModel() // 解構出修飾符// 根據修飾符調整值
const processedModel = computed({get: () => model.value,set: (value) => {if (modifiers.trim) {model.value = value.trim()} else {model.value = value}}
})
</script><template><input v-model="processedModel" />
</template>
- 使用自定義修飾符 .capitalize:
創建一個自定義的修飾符 capitalize,它會自動將 v-model 綁定輸入的字符串值第一個字母轉為大寫
父組件
<Child v-model.capitalize="text" />
子組件??
通過 set 選項處理修飾符邏輯
<script setup>
const [model, modifiers] = defineModel({set(value) {if (modifiers.capitalize) {return value.charAt(0).toUpperCase() + value.slice(1)}return value}
})
</script><template><input type="text" v-model="model" />
</template>
帶參數的 v-model 修飾符
父組件
<UserForm v-model:username.trim="user.name"v-model:age.number="user.age"
/>
子組件??
分別處理每個參數的修飾符:
<script setup>
const [username, usernameModifiers] = defineModel('username')
const [age, ageModifiers] = defineModel('age')// 處理 username 的 trim 修飾符
const processedUsername = computed({get: () => username.value,set: (val) => {username.value = usernameModifiers.trim ? val.trim() : val}
})// 處理 age 的 number 修飾符
const processedAge = computed({get: () => age.value,set: (val) => {age.value = ageModifiers.number ? Number(val) : val}
})
</script><template><input v-model="processedUsername" /><input v-model="processedAge" type="number" />
</template>