【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 在组件中的使用,你就能:

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

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

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

相关推荐
江上月5134 小时前
JMeter中级指南:从数据提取到断言校验全流程掌握
java·前端·数据库
代码猎人4 小时前
forEach和map方法有哪些区别
前端
恋猫de小郭4 小时前
Google DeepMind :RAG 已死,无限上下文是伪命题?RLM 如何用“代码思维”终结 AI 的记忆焦虑
前端·flutter·ai编程
m0_471199634 小时前
【小程序】订单数据缓存 以及针对海量库存数据的 懒加载+数据分片 的具体实现方式
前端·vue.js·小程序
编程大师哥4 小时前
Java web
java·开发语言·前端
A小码哥4 小时前
Vibe Coding 提示词优化的四个实战策略
前端
Murrays4 小时前
【React】01 初识 React
前端·javascript·react.js
大喜xi4 小时前
ReactNative 使用百分比宽度时,aspectRatio 在某些情况下无法正确推断出高度,导致图片高度为 0,从而无法显示
前端
helloCat4 小时前
你的前端代码应该怎么写
前端·javascript·架构
电商API_180079052474 小时前
大麦网API实战指南:关键字搜索与详情数据获取全解析
java·大数据·前端·人工智能·spring·网络爬虫