v-model
不仅限于原生表单元素,它同样可以优雅地应用于自定义组件。
但你是否遇到过这些问题?
"为什么我在组件上用
v-model
,数据不更新?"
"我想用checked
而不是value
作为 prop,怎么做?"
本文将手把手教你如何在自定义组件中实现 v-model
,并深入其工作原理。
一、v-model 的本质:语法糖揭秘
✅ 核心概念
v-model
是一个 语法糖 ,它简化了父子组件之间的双向数据绑定。
vue
<custom-input v-model="message" />
等价于:
vue
<custom-input
:value="message"
@input="message = $event"
/>
💡 它的本质是:
props
传入 +$emit
传出。
二、基础实现:创建支持 v-model 的组件
📌 步骤 1:定义组件
vue
<!-- CustomInput.vue -->
<template>
<div class="custom-input">
<input
:value="value"
@input="$emit('input', $event.target.value)"
:placeholder="placeholder"
/>
</div>
</template>
<script>
export default {
name: 'CustomInput',
props: ['value'], // 接收父组件传来的 value
emits: ['input'], // 明确声明触发的事件(Vue 3 推荐)
props: {
value: {
type: String,
default: ''
},
placeholder: {
type: String,
default: '请输入内容'
}
}
};
</script>
📌 步骤 2:在父组件中使用
vue
<!-- ParentComponent.vue -->
<template>
<div>
<h3>消息:{{ message }}</h3>
<custom-input v-model="message" placeholder="请输入消息" />
</div>
</template>
<script>
import CustomInput from './CustomInput.vue';
export default {
components: { CustomInput },
data() {
return {
message: 'Hello'
};
}
};
</script>
🔁 数据流解析
- 父 → 子 :
message
→value
prop; - 子 → 父 :用户输入 → 触发
input
事件 →$emit('input', 新值)
→ 父组件更新message
。
三、进阶用法:自定义 prop 和事件名称
📌 问题场景
如果组件本身需要 value
prop 用于其他用途(如复选框),怎么办?
✅ 解决方案:使用 model
选项(Vue 2)
vue
<!-- ToggleSwitch.vue -->
<template>
<label class="switch">
<input
type="checkbox"
:checked="isChecked"
@change="$emit('change', $event.target.checked)"
/>
<span class="slider"></span>
</label>
</template>
<script>
export default {
name: 'ToggleSwitch',
model: {
prop: 'isChecked', // 自定义 prop 名
event: 'change' // 自定义 event 名
},
props: {
isChecked: {
type: Boolean,
default: false
}
}
};
</script>
vue
<!-- 使用 -->
<toggle-switch v-model="isOn" />
<!-- 等价于 -->
<toggle-switch
:is-checked="isOn"
@change="isOn = $event"
/>
四、Vue 3 中的 v-model 变化
Vue 3 对 v-model
进行了重大升级:
- 不再默认使用
value
prop; - 支持多个
v-model
绑定; - 默认 prop 名为
modelValue
,事件名为update:modelValue
。
📌 Vue 3 写法
vue
<!-- CustomInput.vue (Vue 3) -->
<template>
<input
:value="modelValue"
@input="$emit('update:modelValue', $event.target.value)"
v-bind="$attrs"
/>
</template>
<script>
export default {
props: ['modelValue'],
emits: ['update:modelValue']
};
</script>
vue
<!-- 父组件 -->
<custom-input v-model="message" />
<!-- 等价于 -->
<custom-input
:model-value="message"
@update:model-value="message = $event"
/>
💡 使用 defineModel
(实验性,Vue 3.4+)
vue
<script setup>
const model = defineModel();
// model.value 即绑定的值
</script>
<template>
<input
:value="model.value"
@input="model.value = $event.target.value"
/>
</template>
五、实战案例:支持 v-model 的下拉选择器
vue
<!-- CustomSelect.vue -->
<template>
<select :value="modelValue" @change="handleChange">
<option value="">请选择</option>
<option
v-for="option in options"
:key="option.value"
:value="option.value"
>
{{ option.label }}
</option>
</select>
</template>
<script>
export default {
props: {
modelValue: [String, Number],
options: {
type: Array,
required: true
}
},
emits: ['update:modelValue'],
methods: {
handleChange(e) {
this.$emit('update:modelValue', e.target.value);
}
}
};
</script>
vue
<!-- 使用 -->
<custom-select
v-model="selectedCity"
:options="cityOptions"
/>
六、最佳实践与注意事项
✅ 最佳实践
- 明确
emits
:在 Vue 3 中,建议显式声明emits
; - 合理命名:避免 prop 名称冲突;
- 支持修饰符 :如
.trim
、.number
,可在组件内处理。
❌ 常见错误
- 忘记定义
value
prop; $emit
的事件名写错(如input
写成oninput
);- 在 Vue 3 中仍使用
value
而非modelValue
。
💡 结语
"v-model 是组件通信的优雅桥梁。"
要点 | 说明 |
---|---|
本质 | :prop + @event 的语法糖 |
Vue 2 | 默认 value prop + input event |
Vue 3 | 默认 modelValue prop + update:modelValue event |
自定义 | 使用 model 选项或 defineModel |
掌握 v-model
在组件中的使用,你就能:
✅ 创建可复用的表单组件;
✅ 实现复杂的双向数据流;
✅ 提升开发效率与代码可读性。