v-model组件传递

一、透传与props的局限性

透传与props在父传子中,除对象或数组型变量外,其它变量只读。
因此在透传与props中对父组件传参的改写相当繁锁,一般采用如下方法:

1、对象或数组实现改写

将传参放在对象或数组型变量中,代码如下:
父组件

<template>
  <HelloWor :uuu="foo" />
  {{foo.fo}}
</template>
<script setup>
import HelloWor from './Hello.vue'
import{ref}from 'vue'
const foo=ref({fo:{}})
</script>

子组件

<template>
    <input v-model="$attrs.uuu.fo" />
</template>
<script setup></script>

2、通过函数实现改写

向子组件传递改写传参的函数,代码如下:
父组件

<template>
  <HelloWor :uuu="foo" :tt="oo" />
  {{foo}}
</template>
<script setup>
import HelloWor from './Hello.vue'
import{ref}from 'vue'
const foo=ref()
function oo(a){if(a.target){foo.value=a.target.value}else{foo.value=a}}
</script>

子组件

<template>
  <input :value="$attrs.uuu" @input="$attrs.tt"/>
  <button @click="ui">测试</button>
</template>
<script setup>
import { useAttrs } from 'vue'
const attrs = useAttrs()
function ui(){attrs.tt('我来改父参')}
</script>

二、v-model传值

v-model传值是一种隐性打包传值。他会隐式的向子传递两个属性(父变量属性,修饰符对象属性)和一个自定义事件(子改写父变量函数)。
<He v-model:参数.修饰符=父变量/>,实际是<He 参数=父变量 参数Modifiers={修饰符:​true} @update:参数=(a)=>{父变量=a}>
隐式的属性与自定义事件在父中不可见,不可用。

(一)v-model书写规范

v-model:参数.修饰符=父变量
参数是传给子组件时的属性标识。
v-model会向子传递参数=父变量属性、参数Modifiers={修饰符:​true}属性、@onUpdate:参数=(a)=>{父变量=a}自定义事件:
参数缺省
书写时v-model参数可以缺省,系统会自动添加一个名为"modelValue"的参数。v-model.修饰符=父变量名缺省的参数实际是这样的v-model:modelValue.修饰符=父变量名
一个父标签有多个v-model时,只能有一个v-model使用缺省。
如下,v-model传值时不需要另传一个改写foo变量的函数,改写foo变量的函数会隐式的传递给子组件:
父组件

<template>
  <HelloWor v-model:aaa="foo" />
  {{foo}}
</template>
<script setup>
import HelloWor from './Hello.vue'
import{ref}from 'vue'
const foo=ref()
</script>

子组件

<template>
  <input v-model="yyy" />
</template>
<script setup>
import{useAttrs,computed}from 'vue'
const attrs = useAttrs()
const emit = defineEmits(['update:aaa'])//v-model父传子必须要用emit声明,否则父的v-model修饰符会不起作用。
const yyy=computed({
get() {return attrs.aaa},
set(newV) {emit('update:aaa',newV)}})
</script>

注意在子组件中使用v-model传递过来的函数名onUpdate:属性标识中有帽号,引用特殊符号的属性要用引号括起且放在[]括号里以数组项的形式才能使用($attrs.update:modelValue会出错,须$attrs['onUpdate:modelValue']。

(二)defineModel函数

Vue 3.4 开始,在子组件中可以使用 defineModel构造函数将v-model传值包装成一个数组形对象(数组包括一个CustomRefImpl对象、一个修饰符对象)。

  • defineModel函数的两个功能
    1、实现CustomRefImpl对象的value属性与v-model传值的双向响应。
    2、对v-model传值较验与自定义get与set。
  • defineModel函数可传递两个参数
    1、v-model传值名。
    2、较验项。
    defineModel('v-model传值名',{v-model传值较验项,自定义get,set})
    注意参数‘v-model传值名’要加引号
    在子组件中用defineModel函数创建对象如下:
    子组件

    <template>
    <button @click="uuu">测试</button>
    <input v-model="yyy" />
    </template>
    <script setup>
    const yyy=defineModel('aaa',{})
    function uuu(){yyy.value='我是新值'}
    </script>
  • defineModel对象原理
    通过上例与'(一)v-model书写规范'实列比对可发现,两者都实现了对象的value属性与v-model传值的双向响应。
    defineModel函数双向响应实现原理应与computed函数类似,只是defineModel构造时不必传递自定义get与set。
    1、内含get函数get(){return v-model传值},依赖v-model传值,当v-model传值改变时,原样返回v-model传值给对象的value属性。
    2、内含set函数set(写入值){return 写入值},当写入value值时,原样返回写入值给value属性。
    3、当value属性改变时,告知DOM重读对象value属性值,调用子改写父变量函数update:参数(a){父变量=a}改写父变量。
  • 修饰符对象解构
    可通过const [a,b] =CustomRefImpl对象的方式解构出修饰符对象。
    数组中a=CustomRefImpl对象,b=修饰符对象。
    修饰符对象内含修饰符属性(属性值是布尔值true),当v-model不带修饰符时,此对象没有修饰符属性。
    修饰符对象解构可在defineModel函数构造对象时进行解构,也可在CustomRefImpl对象上解构。
    在构造时解构,可以在自定义set与get时使用。

(三)defineModel书写规范

CustomRefImpl对象名=defineModel('v-model传值名',{v-model传值较验项,自定义get,自定义set})

  • 1、v-model传值名
    当父v-model的参数缺省时,传值名modelValue也可缺省,如下CustomRefImpl对象名=defineModel({v-model传值较验项,set较验项})
  • 2、v-model传值较验项
    v-model传值较验项同defineProps较验项,主要是对v-model传值进行较验。可较验:type 数据类型、default 默认值、required 必须传值、validator 自定义验证函数。
  • 3、自定义get
    构造的CustomRefImpl对象内含get(){return v-model传值}。作用是以v-model传值为依赖,当v-model传值改变时,把v-model传值返回给value属性。
    defineModel构造对象时也可自定义get,自定义get会覆盖掉对象内默认的get。
    自定义get的作用:
    a、 对“v-model传值”进行除type、default、required、validator之外的复杂较验(type、default、required、validator等4种较验也可在自定义get里实现,但书写更繁杂,没必要)。
    b、 对v-model传值进行条件判定或计算后返回(默认是把v-model传值原样返回给value)。
  • 4、自定义set
    构造的CustomRefImpl对象内含set(写入值){return 写入值}。作用是把value属性的写入值返回给value属性。
    defineModel构造对象时也可自定义的set,自定义set会覆盖掉对象内默认的set。
    自定义set的作用:
    a、 对“value属性的写入值”进行:type、default、required、validator等4种较验。(‘v-model传值较验’可在get函数外实现,而“value属性的写入值”较验必须在set函数里才能实现)。
    b、 对写入值进行条件判定或计算后返回(默认是把写入值原样返回给value)。
    自定义get与set模似默认get与set实例如下:
    父组件

    <template>
    <HelloWor v-model:aaa="foo" />
    <button @click="as">父参变化</button><br>
    我是父{{foo}}
    </template>
    <script setup>
    import HelloWor from './Hello.vue'
    import { ref } from 'vue'
    const foo=ref(1)
    function  as(){foo.value++}
    </script>

    子组件

    <template>
     <button @click="aas">子变化</button><br>
    <h1>我是子{{model}}</h1>
    </template>
    <script setup>
    const Prop=defineProps(['aaa'])
    const model= defineModel('aaa',{
    get() {return Prop.aaa},
    set(value) {return value}
    })
    function  aas(){model.value--}
    </script>

    如下,用自定义get或set实现条件判定或计算后返回。实例如下:

    <script setup>
    const Prop=defineProps(['aaa'])
    const model= defineModel('aaa',{
    get() {if(Prop.aaa<8){return Prop.aaa}else{return '比8超了'+(Prop.aaa-8)}},
    set(value) {if(Prop.aaa<8){return value}else{return Prop.aaa-1}}
    })
    function  aas(){model.value--}
    </script>
    <template>
     <button @click="aas">子变化</button><br>
    <h1>我是子{{model}}</h1>
    </template>

(四)自定义修饰符

  • 1、v-model修饰符
    v-model在input输入框时可以使用3个修饰符,他们分别是:
    .lazy: 输入完成(光标离开或enter回车后),将值赋给绑定变量。
    .number: 字符串类型的数字转换为Number类型后,将值赋给绑定的变量。
    .trim: 过滤首尾空格后,将值赋给绑定的变量。
    组件v-model只有number与trim两修饰符有效,lazy在组件中使用无效。
    组件v-model修饰符原理: 在子改写父变量函数给父变量赋值前判定是否有修饰符,如有就对(回传值)进行修饰处理,再将回传值赋给父变量,如:@update:参数=(a)=>{if(有修饰符){a=修饰函数};父变量=a}。
  • 2、自定义修饰符
    组件v-model可以使用自定义修饰符,自定义修饰符并不会对回传值修饰,只是一个开关。
    set类似于修饰符: 在子组件中,可以通过set对defineModel对象的value属性值(回传值)进行修饰处理(这类似于v-model修饰符的作用)。
    如果set无条件返回修饰处理值,v-model自定义修饰符有没有都会向父返回修饰处理值。
    set可以用自定义修饰符作为返回修饰处理值的条件,当父有自定义修饰符返回修饰处理值,无自定义修饰符返回未修饰处理的值(类似于一个开关的作用)。
    自定义修饰符实例如下:
    父组件

    <template>
    <HelloWor v-model:aaa.wocao="foo" />
    <button @click="as">父参变化</button><br>
    我是父{{foo}}
    </template>
    <script setup>
    import HelloWor from './Hello.vue'
    import { ref } from 'vue'
    const foo=ref(1)
    function  as(){foo.value='压力'}
    </script>

    子组件

    <template>
    <button @click="aas">子变化</button><br>
    <input v-model="model">
     <h1>我是子{{model}}</h1>
     </template>
     <script setup>
     const [model,yh]= defineModel('aaa',{
      set(value) {if(yh.wocao){return value+'我经修饰了'};return 'value'}
     })
     function  aas(){model.value='8'}
     </script>

百分之一百零八
15 声望3 粉丝