模板指令-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>

 

posted @ 2021-11-10 15:30  keyeking  阅读(266)  评论(0编辑  收藏  举报