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>
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。