vue3中v-model的用法详解
绑定单个属性
基础绑定
以 自定义组件 CustomInput
举例
<script setup>
const txt = ref('');
</script>
<template>
<CustomInput v-model="txt" />
</template>
v-model
会被展开为如下的形式
<CustomInput
:modelValue="txt"
@update:modelValue="newValue => txt = newValue"
/>
<CustomInput>
组件内部需要做两件事:
- 将内部原生
<input>
元素的value
attribute 绑定到modelValue
prop - 当原生的
input
事件触发时,触发一个携带了新值的update:modelValue
自定义事件
这里是相应的代码:
<script setup>
const props = defineProps({
'modelValue': String,
})
const emit = defineEmits(["update:modelValue"])
</script>
<template>
<input :value="modelValue" @input="$emit('update:modelValue', $event.target.value)" />
</template>
有些人会觉得这种写法过于繁琐,会导致标签代码变得冗长
另一种在组件内实现 v-model
的方式是使用一个可写的,同时具有 getter 和 setter 的 computed
属性
computed 绑定
使用computed
属性时, get
方法需返回 modelValue
prop,而 set
方法需触发相应的事件
<script setup>
const value = computed({
get() {
return props.modelValue
},
set(value) {
emit("update:modelValue", value)
}
})
</script>
<template>
<input v-model="value" />
</template>
这种写法可以简化标签中的属性,逻辑清晰
单个属性可以使用 v-model
轻松搞定,如果多个属性都需要双向绑定呢?
? v-model 绑定多个属性
默认情况下,v-model
在组件上都是使用 modelValue
作为 prop,并以 update:modelValue
作为对应的事件
但我们可以通过给 v-model
指定一个参数来更改这些名字:
<template>
<CustomInput v-model:first-name="first" v-model:last-name="last" />
</template>
同样的,也可以用两种方式绑定,只是 prop
从原来的 modelValue
变为了传入的参数名,对应的事件也变成了 update:参数名
<script setup>
const props = defineProps({
firstName: String,
lastName: String,
})
// 在computed中 使用
const emit = defineEmits(['update:firstName', 'update:lastName'])
</script>
<template>
<input
type="text"
:value="firstName"
@input="$emit('update:firstName', $event.target.value)"
/>
<input
type="text"
:value="lastName"
@input="$emit('update:lastName', $event.target.value)"
/>
</template>
? 绑定对象
在一个复杂的组件中,如果多个字段需要双向绑定,如果使用上文所示方法的话,会有一些繁琐
介绍两种双向绑定对象的做法
定义父组件 searchBar
为一个复杂表单组件
<script setup>
import { ref } from "vue"
const modelValue = ref({
keyword: "123",
selectValue: "",
options: [
{
label: "全部",
value: ""
},
{
label: "a1",
value: "1"
},
{
label: "a2",
value: "2"
},
]
})
</script>
<template>
<searchBar v-model="modelValue" />
</template>
那么在 searchBar
组件内,我们接收 modelValue
并定义类型为 Object
<template>
<div>
<!-- <input type="text" v-model="modelValue.keyword"> 可以实现双向绑定 -->
<input type="text"
:value="modelValue.keyword"
@input="handleKeywordChange"
>
<select v-model="modelValue.selectValue">
<option v-for="o in modelValue.options" :key="o.value" :value="o.value">
{{ o.label }}
</option>
</select>
</div>
</template>
<script lang="ts" setup>
const props = defineProps({
modelValue: {
type: Object,
default: () => ({})
}
})
const emit = defineEmits(["update:modelValue"]);
// 以 input 举例
const handleKeywordChange=(val)=>{
emit("update:modelValue",{
...props.modelValue,
keyword:val.target.value
})
}
</script>
如果传入对象的话,如注释所介绍的那样
<input type="text" v-model="modelValue.keyword">
虽然可以直接进行双向绑定,但是这样会破坏单项数据流
和上文的 emit
触发事件一样,但是传递的数据则变成了对象
虽然使用 emit 可以触发双向绑定,但是过于繁琐,下面介绍一种更优雅的写法,可以说是一种奇技淫巧 -- computed + prxoy
如果使用 computed
绑定,你可能会写出这种代码
<template>
<input type="text" v-model="model.keyword">
</template>
<script lang="ts" setup>
const model = computed({
get() {
return props.modelValue
},
set(value) {
// console.log(value) // 发现没有打印
emit("update:modelValue", {
...props.modelValue,
keyword: value
})
}
})
<script>
但是当你输入的时候,你会发现并没有触发 setter
, 因为 computed
会做一层代理,代理对象没有发生修改
如果想要触发 setter
,如下图:
// 只有这样才会变化
model.value = {
keyword:"asdfad"
}
这种方法无法触发 setter
,也就无法双向绑定,该怎么办呢?
在 getter
中返回 一个 代理对象!在 getter
中返回 一个 代理对象!在 getter
中返回 一个 代理对象!
因为 proxy
代理的对象是和被代理对象属性是保持一致的,所以我们使用 proxy
包裹原对象
那么 v-model
绑定的是代理之后的对象,如果代理对象属性发生了改变,则会触发代理对象中的 set
方法,此时我们可以触发 emit
const model = computed({
get() {
return new Proxy(props.modelValue, {
set(obj, name, val) {
emit("update:modelValue", {
...obj,
[name]: val
})
return true
}
})
},
set(value) {
emit("update:modelValue", {
...props.modelValue,
keyword: value
})
}
})
修饰符
我们知道 v-model
有一些内置的修饰符,例如 .trim
,.number
和 .lazy
。
在某些场景下,我们可能想要一个自定义组件的 v-model
支持自定义的修饰符。
我们来创建一个自定义的修饰符 capitalize
,它会自动将 v-model
绑定输入的字符串值第一个字母转为大写:
<CustomInput v-model.capitalize="txt" />
我们添加了capitalize
修饰符,他会被自动传入到 prop
中的 modelModifiers
中
<script setup>
const props = defineProps({
modelValue: String,
modelModifiers: {
default: () => ({})
}
})
const 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 :value="modelValue" @input="emitValue" />
</template>
总结
在 vue
中, 我们可以使用 v-model
指令进行很方便的双向绑定,配合修饰符可以完成很多有趣的功能
以上就是vue3中v-model的用法详解的详细内容,更多关于vue3 v-model用法 的资料请关注编程网其它相关文章!
免责声明:
① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。
② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341