在vue2中一个组件上只能挂载一个v-model,而vue3中对v-model进行了优化,可写多个v-model,用自定义修饰符进行识别。
在普通组件上使用:只有作为表单内的元素属性时才会生效 如: input 、radio 、checkbox
<input v-model="searchText" />
//相当于
<input
:value="searchText"
@input="searchText = $event.target.value"
/>
在自定义组件上使用:
<CustomInput v-model="searchText"/>
//相当于
<CustomInput
:model-value="searchText"
@update:model-value="newValue => searchText = newValue"
/>
对自定义组件v-model接收
//父组件
<CustomInput v-model="searchText"/>
//子组件接收
interface IProps {
modelValue: boolean;
}
const props = withDefaults(defineProps<IProps>(), {
modelValue: true,
});
const emit = defineEmits(["update:modelValue"]);
//父组件
<CustomInput v-model:textModel="textModel"/>
//子组件接收
interface IProps {
textModel: string;
}
const props = withDefaults(defineProps<IProps>(), {
textModel: '',
});
const emit = defineEmits(["update:textModel"]);
//父组件
<Address v-model="showModal" v-model:textModel.addPrefix="textModel" />
//子组件
//自定义的修饰符需要在子组件中用 props 接收 modelModifiers 对象,这个对象下面会有自定义属性,值为 true
const props = (defineProps<{
textModelModifiers:{
addPrefix:boolean
}
}>();
cons
在子组件中不要直接修改父组件传过来的props,如下,直接使用会报以下的错误
请注意这个错误在版本3.2.45上开始出现。
对于实现模式,正如文档中所指出的,props在组件中应该被认为是只读
的,Vue在版本3.2.45之前没有充分地执行它。
针对上面案例中出现的问题,我们明白了在子组件不能直接修改父组件的传过来的值。那如何更好的实现双向数据绑定呢,这个时候我们可以结合computed一起使用。
如下,是关于v-model和computed结合使用的例子
效果图
v-model数据双向绑定
父组件实现代码
//父组件
<template>
<n-message-provider>
<button @click="handleEdit">编辑button>
<div>个人简介:{{ textModel }}div>
<Modal v-model="showModal" v-model:textModel"textModel" />
n-message-provider>
template>
<script setup lang="ts">
import { ref } from "vue";
import { NMessageProvider } from "naive-ui";
import Modal from "@/views/Modal /Modal.vue";
const showModal = ref(false);
const textModel = ref("");
const handleEdit = () => {
showModal.value = true;
};
script>
<style scoped lang="scss">style>
在上面代码中引入了modal子组件,并向子组件绑定了默认的v-model
和自定义名称的v-model:textModel
子组件实现代码
<template>
<n-modal
v-model:show="model_value"
preset="card"
:title="title"
size="small"
class="modal-dialog-wrapper"
header-style="padding: 10px 20px"
:bordered="false"
:segmented="segmented"
display-directive="show"
:style="{ width: contentWidth }"
>
<div class="content" :style="{ height: '60vh', height: contentHeight }">
<n-scrollbar class="pl-5 pr-5">
<p>个人简介:p>
<n-input
v-model:value="text_model"
type="textarea"
placeholder="请输入个人简介"
rows="8"
/>
n-scrollbar>
div>
<template #footer>
<div class="footer-wrap">
<n-space>
<n-button type="primary" size="small" @click="onConfirm"
>确定n-button
>
n-space>
div>
template>
n-modal>
template>
<script setup lang="ts">
import { ref, computed } from "vue";
import { NModal, NScrollbar, NSpace, NButton, NInput } from "naive-ui";
interface IProps {
title?: string;
contentHeight?: string;
contentWidth?: string;
modelValue: boolean;
textModel?: string;
textModelModifiers: {
addPrefix: boolean;
};
}
const props = withDefaults(defineProps<IProps>(), {
title: "消息",
contentHeight: "30vh",
contentWidth: "600px",
modelValue: true,
});
const segmented = ref({
content: "soft",
footer: "soft",
});
const emit = defineEmits(["update:modelValue", "update:textModel"]);
const model_value = computed({
get() {
return props.modelValue;
},
set(value) {
emit("update:modelValue", value);
},
});
const text_model = computed({
get() {
return props.textModel;
},
set(value) {
emit("update:textModel", value);
},
});
const onConfirm = () => {
model_value.value = false;
};
script>
<style scoped>
.footer-wrap {
display: flex;
justify-content: flex-end;
}
style>
在上面子组件中,为了实现双向数据绑定,使用computed将可写属性与 getter 和 setter 一起使用。该方法应返回属性,并且该方法应发出相应的事件。
Vue3 中的 v-model 经历了一些改进,提供了更灵活的用法来实现双向数据绑定。除了原生的表单元素外,v-model 现在可以应用于任意组件,并允许我们自定义 prop 和事件名称。
使用 Vue3 的 v-model,我们可以更加方便地实现组件之间的数据双向同步,减少了手动处理数据绑定的工作量。