【vue篇】Vue 进阶指南:如何在自定义组件中完美使用 v-model

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>

🔁 数据流解析

  1. 父 → 子messagevalue prop;
  2. 子 → 父 :用户输入 → 触发 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" 
/>

六、最佳实践与注意事项

✅ 最佳实践

  1. 明确 emits :在 Vue 3 中,建议显式声明 emits
  2. 合理命名:避免 prop 名称冲突;
  3. 支持修饰符 :如 .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 在组件中的使用,你就能:

✅ 创建可复用的表单组件;

✅ 实现复杂的双向数据流;

✅ 提升开发效率与代码可读性。

相关推荐
LuckySusu3 小时前
【vue篇】单页 vs 多页:Vue 应用架构的终极对决
前端·vue.js
LuckySusu3 小时前
【vue篇】Vue 核心指令原理解析:v-if、v-show、v-html 的底层奥秘
前端·vue.js
LuckySusu3 小时前
【vue篇】Vue v-model 深度解析:从表单到组件的双向绑定之谜
前端·vue.js
LuckySusu3 小时前
【vue篇】Vue 2 响应式系统:Object.defineProperty 的五大缺陷
前端·vue.js
奶糖 肥晨3 小时前
Rokid JSAR 技术开发全指南:基于 Web 技术栈的 AR 开发实战
前端·ar·restful
LuckySusu3 小时前
【vue篇】Vue 中 computed 和 methods 的本质区别:缓存的艺术
前端·vue.js
Python私教3 小时前
React + Ant Design + Tailwind CSS 打造「无痕」垂直滚动区域:功能全上,滚动条隐身
前端·css·react.js
LuckySusu3 小时前
【vue篇】Vue 中 computed 和 watch 的终极对比:何时用谁?
前端·vue.js
LuckySusu3 小时前
【vue篇】Vue 过滤器(Filters)完全指南:优雅处理数据展示
前端·vue.js