概要
聊聊Vue defineModel()的使用
从 Vue 3.4 开始,推荐的实现方式是使用 defineModel() 宏:
- 一个名为
modelValue
的 prop,本地 ref 的值与其同步; - 一个名为
update:modelValue
的事件,当本地 ref 的值发生变更时触发。
技术细节
defineModel() 返回的值是一个 ref。它可以像其他 ref 一样被访问以及修改,不过它能起到在父组件和当前变量之间的双向绑定的作用:
它的 .value 和父组件的 v-model 的值同步;
当它被子组件变更了,会触发父组件绑定的值一起更新。
主要代码
父组件
<script setup>
import { ref } from 'vue'
import MyComponent from './MyComponent.vue'
const title = ref('v-model argument example1111')
</script>
<template>
<h1>{{ title }}</h1>
<MyComponent v-model:title="title" />//通过v-model:title将title传给子组件
<br/>
父组件:
<input type="text" v-model="title"/>//父组件改变值也会及时更新到子组件
</template>
子组件
这样就可以实现父组件与子组件的通信了,而且不管是父组件,还是子组件的值改变,俩者的值始终是一致的,跟使用defineProps搭配defineEmits来实现通信是一样的,但这样简洁了代码,一行代码就可以实现通信了。
//使用defineModel实现通信
<script setup>
const title = defineModel('title')//使用defineModel接收值
</script><template>
子组件:
<input type="text" v-model="title" />
<br/>
子组件:
<div @click="title='子组件数据变化,引起父组件数据变化'">子组件点击事件</div>
</template>
//使用defineProps搭配defineEmits实现通信
在 3.4 版本之前,你一般会按照如下的方式来实现上述相同的子组件:
<script setup>
const props = defineProps(['modelValue'])
const emit = defineEmits(['update:modelValue'])
</script><template>
<input
:value="props.modelValue"
@input="emit('update:modelValue', $event.target.value)"
/>
</template>
拓展
多个 v-model
绑定
利用刚才在 v-model 参数小节中学到的指定参数与事件名的技巧,我们可以在单个组件实例上创建多个 v-model
双向绑定。
组件上的每一个 v-model
都会同步不同的 prop,而无需额外的选项
<UserName
v-model:first-name="first"
v-model:last-name="last"
/>
<script setup>
const firstName = defineModel('firstName')
const lastName = defineModel('lastName')
</script><template>
<input type="text" v-model="firstName" />
<input type="text" v-model="lastName" />
</template>
v-model
修饰符
在某些场景下,你可能想要一个自定义组件的 v-model
支持自定义的修饰符。比如通过传入不同的值执行不同的操作
<UserName
v-model:first-name.capitalize="first"
v-model:last-name.uppercase="last"
/>
<script setup>
const [firstName, firstNameModifiers] = defineModel('firstName')
const [lastName, lastNameModifiers] = defineModel('lastName')console.log(firstNameModifiers) // { capitalize: true }
console.log(lastNameModifiers) // { uppercase: true}if(capitalize){
console.log("capitalize的相关操作")
}else if(uppercase){
console.log("uppercase的相关操作")
}
</script>
也能够基于修饰符选择性地调节值的读取和写入方式,例如:
<MyComponent v-model.capitalize="myText" />
<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>
prop
因为 defineModel
声明了一个 prop,你可以通过给 defineModel
传递选项,来声明底层 prop 的选项:
// 使 v-model 必填
const model = defineModel({ required: true })//使用
const title = defineModel('title', { required: true }) //此时父组件必须传该值即(title),否则浏览器发出警告
// 提供一个默认值
const model = defineModel({ default: 0 })
注意
提示:
如果为 defineModel
prop 设置了一个 default
值且父组件没有为该 prop 提供任何值,会导致父组件与子组件之间不同步。在下面的示例中,父组件的 myRef
是 undefined,而子组件的 model
是 1:
例如:
// 子组件:
const model = defineModel({ default: 1 })// 父组件
const myRef = ref()
<Child v-model="myRef"></Child>
提供先进的推理,复杂的指令,更多的创造力。