模板指令-v-model指令的变化
v-model指令的变化
在vue2中有两个相似的功能,是sync修饰符和v-model,但是实际运用的时候,往往容易混淆,因为它们功能很相似。比如我想绑定一个动态的值,又想在子组件中尝试的去改变它,sync修饰符和v-model都可以实现此功能,所以不知道该选哪个。
在vue3中这两个功能做了一次合并,它将sync修饰符直接一除掉统一为v-model参数形式;
变化:
- 非兼容:用于自定义组件时,v-model属性和事件默认名称被更改
prop: value----->modelValue
event: input---->update:modelValue
- 非兼容:v-bind的sync修饰符和组件的model选项被移除,用v-model来代替
- 新增:可以在同一个组件上使用多个v-model进行双向绑定
- 新增:可以自定义v-model修饰符
1>组件中使用单个v-model
在vue3中,自定义组件上的v-model相当于传递了modelValue属性,并接收抛出的update:modelValue事件:
app.vue
<template> <div> app <child v-model="counter"></child> <!--等同于--> <!-- <child :modelValue="counter" @updata:modeValue="counter=$event"></child> --> </div> </template> <script> import child from "./components/child.vue" import {ref} from "vue" export default { components:{ child }, setup(){ const counter=ref(10) return {counter} } } </script> <style scoped> </style>
child.vue
<template> <div> {{modelValue}} <hr/> <input type="text" :value="modelValue" @input="$emit('update:modelValue',$event.target.value)"/> </div> </template> <script> export default { props:{ modelValue:{ type:Number, default:0 } } } </script> <style scoped> </style>
2>要更改model名称,而不是更改组件内的model选项,那么我们需要将一个argument传递给model
app.vue
<template> <div> app <child v-model:counter="counter"></child> <!--等同于--> <!-- <child :counter="counter" @updata:counter="counter=$event"></child> --> </div> </template>
child.vue
<template> <div> {{counter}} <hr/> <input type="text" :value="counter" @input="$emit('update:counter',$event.target.value)"/> </div> </template> <script> export default { props:{ counter:{ type:Number, default:0 } } } </script>
3>在自定义组件上使用多个v-model
app.vue
<template> <div> app <child v-model:counter="counter" v-model:title="title"></child> <!--等同于--> <!-- <child :counter="counter" @updata:counter="counter=$event" :title="title" @updata:title="title=$event" ></child> --> </div> </template> <script> import child from "./components/child.vue" import {ref} from "vue" export default { components:{ child }, setup(){ const counter=ref(10) const title=ref("自定义组件上使用多个v-model") return {counter,title} } } </script> <style scoped> </style>
child.vue
<template> <div> {{counter}} {{title}} <hr/> <input type="text" :value="counter" @input="$emit('update:counter',$event.target.value)"/> <input type="text" :value="title" @input="$emit('update:title',$event.target.value)"/> </div> </template> <script> export default { props:{ counter:{ type:Number, default:0 }, title:{ type:String } } } </script> <style scoped> </style>
在这里我们会接收到一个警告
类型选择失败,couter应该接收一个number,但是得到的却是一个string,因为我们在这里是通过文本框获取的数值,的到的string类型,所以需要用parseInt转换成number类型
<template> <div> {{counter}} {{title}} <hr/> <input type="text" :value="counter" @input="$emit('update:counter',parseInt($event.target.value))"/> <input type="text" :value="title" @input="$emit('update:title',parseInt($event.target.value))"/> </div> </template>
此时就不会有警告了
4>v-model自定义修饰符
在vue3中支持自定义修饰符。我们在使用表单输入绑定时,我们看到v-model有内置修饰符--.trim、.number、.lazy,但是,在某些情况下,我们可能还需要添加自己的自定义修饰符。
案例:
创建一个自定义修饰符cap,他将v-model绑定提供的字符串的第一个字母大写
因为在vue3中,添加到组件v-model的修饰符将通过modelModifiers提供给组件,在该案例中,我们需要创建一个组件,其中包含默认为空对象的modelModifiers属性
注意:当组件的setup中触发时,modelModifiers属性包含了cap,它的值为true。
cap.vue
<template> <div> <input type="text" :value="modelValue" @input="$emit('update:modelValue',$event.target.value)"/> </div> </template> <script> import { getCurrentInstance } from 'vue' export default { props:{ modelValue:String, //组件接收属性modelModifiers对象,对象中会包含多个修饰符,这里默认给个空对象 {} modelModifiers:{ default:()=>{} } }, setup(props){ const {ctx}=getCurrentInstance() console.log(ctx.modelModifiers); //{capitalize:true} } } </script>
app.vue
<template> <div> app <cap v-model.capitalize="modelValue"></cap> </div> </template> <script> import cap from "./components/cap.vue" import {ref} from "vue" export default { components:{ cap }, setup(){ const test=ref('hello') return {test} } } </script>
案例中组件接收一个属性modelModifiers对象,然后再setup中拿到了修饰符capitalize值为true,实现修饰符capitalize,将v-model绑定的值的首字母大写
cap.vue
<template>
<div>
<input type="text"
:value="modelValue"
@input="emitValue"/>
</div>
</template>
<script>
import { getCurrentInstance } from 'vue'
export default {
props:{
modelValue:String,
//组件接收属性modelModifiers对象,对象中会包含多个修饰符,这里默认给个空对象 {}
modelModifiers:{
default:()=>{}
}
},
setup(props){
const {ctx}=getCurrentInstance()
console.log(ctx.modelModifiers);
function emitValue(e){
let v=e.target.value;
if(ctx.modelModifiers.capitalize){
v=v.charAt(0).toUpperCase()+v.slice(1)
}
ctx.$emit('update:modelValue',v)
}
return {emitValue}
}
}
</script>
此时我们在输入任意字母,默认第一个都为大写
vue3支持组件同时绑定多个带参数的v-model,所以在带参数的情况下,修饰符属性命名变为参数名+'Modifiers'
app.vue
<template> <div> app <cap v-model.capitalize="test"></cap> <hr/> <all v-model:title.capitalize="title" v-model:test.capitalize="title"></all> </div> </template> <script> import cap from "./components/cap.vue" import all from "./components/all.vue" import {ref} from "vue" export default { components:{ cap, all }, setup(){ const counter=ref(10) const title=ref("title") const test=ref('hello') return {counter,title,test} } } </script> <style scoped> </style>
all.vue
<template> <div> title: <input type="text" :value="title" @input="$emit('update:title',$event.target.value)"> test: <input type="text" :value="test" @input="$emit('update:test',$event.target.value)"> </div> </template> <script> import { getCurrentInstance } from '@vue/runtime-core' export default { props:['title','titleModifiers','test','testModifiers'], setup(){ const {ctx}=getCurrentInstance(); console.log(ctx.titleModifiers,"多个v-model"); console.log(ctx.testModifiers); } } </script> <style scoped> </style>
app.vue
<template> <div> app <cap v-model.capitalize="test"></cap> <hr/> <all v-model:title.capitalize="title" v-model:test.upper="test" ></all> </div> </template> <script> import cap from "./components/cap.vue" import all from "./components/all.vue" import {ref} from "vue" export default { components:{ cap, all }, setup(){ const counter=ref(10) const title=ref("title") const test=ref('hello') return {counter,title,test} } } </script> <style scoped> </style>
all.vue
<template> <div> <p> title: <input type="text" :value="title" @input="emitCapitalize"/> </p> <p> test: <input type="text" :value="test" @input="emitUpper"/> </p> </div> </template> <script> import { getCurrentInstance } from 'vue' export default { props:['title','titleModifiers','test','testModifiers'], setup(props){ const {ctx}=getCurrentInstance(); //console.log(ctx.titleModifiers,"多个v-model"); //console.log(ctx.testModifiers,"多个v-model"); // 首字母大写 function emitCapitalize(e){ let v=e.target.value; if(ctx.titleModifiers.capitalize){ v=v.charAt(0).toUpperCase()+v.slice(1) } ctx.$emit('update:title',v) } // 字母全部大写 function emitUpper(e){ let v=e.target.value; if(ctx.testModifiers.upper){ v=v.toUpperCase() } ctx.$emit('update:test',v) } return {emitCapitalize,emitUpper} } } </script>