v-model 不仅限于原生表单元素,它同样可以优雅地应用于自定义组件

但你是否遇到过这些问题?

本文将手把手教你如何在自定义组件中实现 v-model,并深入其工作原理。


一、v-model 的本质:语法糖揭秘

核心概念

v-model 是一个 语法糖,它简化了父子组件之间的双向数据绑定

<custom-input v-model="message" />

等价于

<custom-input
  :value="message"
  @input="message = $event"
/>

二、基础实现:创建支持 v-model 的组件

步骤 1:定义组件

<!-- 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:在父组件中使用

<!-- 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>

数据流解析

  1. 父 → 子messagevalue prop;
  2. 子 → 父:用户输入 → 触发 input 事件 → $emit('input', 新值) → 父组件更新 message

三、进阶用法:自定义 prop 和事件名称

问题场景

如果组件本身需要 value prop 用于其他用途(如复选框),怎么办?

解决方案:使用 model 选项(Vue 2)

<!-- 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>
<!-- 使用 -->
<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 写法

<!-- 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>
<!-- 父组件 -->
<custom-input v-model="message" />
<!-- 等价于 -->
<custom-input
  :model-value="message"
  @update:model-value="message = $event"
/>

使用 defineModel(实验性,Vue 3.4+)

<script setup>
const model = defineModel();
// model.value 即绑定的值
</script>

<template>
  <input 
    :value="model.value" 
    @input="model.value = $event.target.value" 
  />
</template>

五、实战案例:支持 v-model 的下拉选择器

<!-- 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>
<!-- 使用 -->
<custom-select 
  v-model="selectedCity" 
  :options="cityOptions" 
/>

六、最佳实践与注意事项

最佳实践

  1. 明确 emits:在 Vue 3 中,建议显式声明 emits
  2. 合理命名:避免 prop 名称冲突;
  3. 支持修饰符:如 .trim.number,可在组件内处理。

常见错误

  • 忘记定义 value prop;
  • $emit 的事件名写错(如 input 写成 oninput);
  • 在 Vue 3 中仍使用 value 而非 modelValue

结语

要点说明
本质:prop + @event 的语法糖
Vue 2默认 value prop + input event
Vue 3默认 modelValue prop + update:modelValue event
自定义使用 model 选项或 defineModel

掌握 v-model 在组件中的使用,你就能:

创建可复用的表单组件;
实现复杂的双向数据流;
提升开发效率与代码可读性。

本站提供的所有下载资源均来自互联网,仅提供学习交流使用,版权归原作者所有。如需商业使用,请联系原作者获得授权。 如您发现有涉嫌侵权的内容,请联系我们 邮箱:[email protected]