v-model
1,与表单进行双向数据绑定
<script setup> import { ref } from 'vue' const msg = ref('Hello World!') </script> <template> <h1>{{ msg }}</h1> <input v-model="msg" /> </template>
相当于做了两件事,1,将msg绑定input的value上;2,input的value变更时,将新value赋值给msg,类似下面的::value="msg" @input="updateValue"
2,与子组件进行双向数据绑定
<!-- Parent.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> <!-- Child.vue --> <script setup> const model = defineModel() </script> <template> <span>My input</span> <input v-model="model"> </template> //相当于下面的老版本(3.4之前)的实现方式 <!-- Parent.vue --> <Child :modelValue="msg" @update:modelValue="$event => msg = $event" /> <!-- 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>
//注意:父组件传的值为undefined,子组件有默认值,这种情况将导致父组件的model跟子组件的model初始值不一致,不过,绑定关系还在,双方任何改动,还是会使值同步。如下 // 子组件: const model = defineModel({ default: 1 }) // 父组件 const myRef = ref() <Child v-model="myRef"></Child>
defineModel:获取父元素传过来的属性值,用法如下
父:<Child v-model='aaa'> //不带参数,默认参数名称是modelValue , 相当于 <Child v-bind:modelValue='aaa' v-on:update:modelValue='value=>modelValue=value'>
1,子:let model = defineModel()
2,子带默认值:let model = defineModel({ default: 0 })
3,父必填:let model = defineModel({ required: true })
父带参数:<Child v-model:first-name='aaa' ></Child> //不带参数,默认参数名称是modelValue
子:let model = defineModel(‘firstName’,{ default: 0 }); //first-name 可用firstName接收
父多个绑定:<Child v-model:first-name='aaa' v-model:last-name='bbb'></Child>
子:let model = defineModel(‘firstName’); let model = defineModel(‘lastName’);
v-model可带修饰符:trim number lazy,及自定义修饰符
//带指令基础写法 <!--Parent--> const myText = ref('') <Child v-model.trim="myText" /> <!--Child--> const model = defineModel(); <input type="text" v-model="model" />
//自定义指令,例如capitalize,把输入的值首字母大写 <!--Parent--> <Child v-model.capitalize="myText" /> <!--Child--> <script setup> //modifiers打印的值为:{ capitalize: true },与自定义修饰符保持一致 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老版本写法如下: <!--Parent--> let myText = ref(''); <Child v-model.capitalize='myText' /> <!--Child--> <script setup> const props = defineProps({ modelValue: String, modelModifiers: { default: () => ({}) } }) const emit = defineEmits(['update:modelValue']) function 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 type="text" :value="modelValue" @input="emitValue" /> </template>
v-model可以同时添加参数及修饰符,例如 let msg = ref(''), v-model:name.trim="msg"
总结:
表单:v-model是:value="msg" @input="updateValue" 的缩写
let updateValue = ($e)=>{
msg.value = $e.target.value
}
子组件:v-model是:modelValue="msg" @update:modelValue="$value => msg = $value " 的缩写
let msg = ref('');
<Child v-model='msg'/> 跟 <Child :modelValue="msg" @update:modelValue="$value => msg = $value " />一样
defineModel
是一个便利宏。编译器将其展开为以下内容:
- 一个名为
modelValue
的 prop,本地 ref 的值与其同步; - 一个名为
update:modelValue
的事件,当本地 ref 的值发生变更时触发
及defineModel只用于子组件中, 是下面两句的缩写
const props = defineProps(['modelValue'])
const emit = defineEmits(['update:modelValue'])