v-model
的本质在 Vue3 中,v-model
是语法糖,本质是通过 modelValue
prop 和 update:modelValue
事件实现父子组件间的双向数据绑定。
举个,以下两种写法等价:
// 父组件
<ChildComponent v-model="value">
// 等价于
<ChildComponent
:modelValue="value"
@update:modelValue="newValue => value = newValue"
>
v-model
实现// 子组件
<script setup>
const props = defineProps(['modelValue']);
const emit = defineEmits(['update:modelValue']);
// 通过计算属性或方法触发更新
const valueProxy = computed({
get() {
return props.modelValue;
},
set(val) {
emit('update:modelValue', val)
}
});
</script>
<template>
<input v-model="valueProxy" />
</template>
核心逻辑:
modelValue
prop 接收父组件值update:modelValue
事件向父组件传递新值使用 defineModel()
宏(需启用实验性功能)
<script setup>
const modalValue = defineModal();
</script>
<template>
<input v-model="modelValue" />
</template>
优势:无需手动定义 props/emits,代码更简洁
v-model
绑定Vue3 支持为同一组件绑定多个 v-model
,适用于复杂组件
// 父组件
<UserForm
v-model:name="userName"
v-model:age="userAge"
/>
// 子组件
<script setup>
const name = defineModel('name');
const age = defineModel('age');
</script>
通过 modelModifiers
实现自定义修饰符
// 父组件
<TextInput v-model.trim.uppercase="text" />
// 子组件
<script setup>
const props = defineProps({
modelValue: String,
modelModifiers: { defualt: () => ({}) }
});
const emit = defineEmits(['update:modelValue']);
function handleInput(e) {
let value = e.target.value;
if(props.modelModifiers.uppercase) {
value = value.toUpperCase();
}
emit('update:modelValue', value);
}
</script>
直接绑定对象属性(需注意响应性):
// 父组件
<UserForm v-model="user" />
// 子组件
<script setup>
const props = defineProps(['modelValue']);
const emit = defineEmits(['update:modelValue']);
// 修改深层属性
function updateName(name) {
emit('update:modelValue', {
...props.modelValue,
name
})
}
v-model
+ emit
显式更新// 子组件
<button @click="$emit('update:modelValue', newValue)">
提交
</button>
ref
+ 事件监听// 父组件
<ChildComponent :value="data" @change="handleChange" />
// 子组件
<script setup>
defineProps(['value']);
const emit = defineEmits(['change']);
function updateValue(val) {
emit('change', val)
}
</script>
useVModel
(VueUse 工具库)import { useModel } from '@vueuse/core';
const value = useModel(props, 'modelValue', emit);
watch
时注意设置 deep: true
defineProps<{ modelValue: boolean }>();
defineEmits<{ (e: 'update:modelValue', val: boolean): void}>()
// 自定义开关组件
<template>
<div
class="switch"
:class="{ active: modelValue }"
@click="toggle"
></div>
</template>
<script setup>
const props = defineProps(['modelValue']);
const emit = defineEmits(['update:modelValue']);
const toggle = () => {
emit('update:modelValue', !props.modelValue);
};
</script>
<template>
<div v-show="modelValue" class="model">
<slot />
<button @click="$emit('update:modelValue', false)">关闭</button>
</div>
</template>
方法 | 适用场景 | 优点 | 注意事项 |
---|---|---|---|
标准v-model |
简单双向绑定 | 语法简洁 | 只能绑定单个值 |
多个v-model |
多值控制的复杂组件 | 清晰分离关注点 | 需要合理命名参数 |
defineModel 宏 |
Vue 3.3+ 项目 | 极简代码 | 需启用实验性功能 |
useModel 工具 |
需要额外逻辑控制 | 灵活扩展 | 依赖 VueUse 库 |
通过灵活选择双向绑定方案,可以显著提升组件代码的可维护性和开发效率。核心原则是:在保证数据流清晰的前提下,选择最简洁的实现方式。