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

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

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

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

相关推荐
是上好佳佳佳呀1 分钟前
【前端(十二)】JavaScript 函数与对象笔记
前端·javascript·笔记
你真的快乐吗16 分钟前
@fuxishi/svg-icon:一个 Vue 3 svg本地图标+iconify图标组件库,让图标管理不再头疼
前端·vue.js·typescript
Rkgua22 分钟前
ESModule和Commonjs模块的区别
前端·javascript
江南十四行22 分钟前
ReAct Agent 基本理论与项目实战(二)
前端·react.js·前端框架
用户6000718191028 分钟前
【翻译】React 如何乱序流式输出 UI,却仍保持最终顺序
前端
江南十四行38 分钟前
AI Agent应用类型及Function Calling开发实战(三)
服务器·前端·javascript
GISer_Jing41 分钟前
AI原生全栈架构理论体系:从分布式范式演进到全链路工程化理论基石
前端·人工智能·学习·ai编程
GISer_Jing43 分钟前
从“切图仔”到“增长架构师”:AI时代营销前端的范式革命
前端·人工智能·ai编程
广州华水科技1 小时前
单北斗GNSS在水库变形监测中的应用与系统安装解析
前端
xingpanvip1 小时前
星盘接口开发文档:组合三限盘接口指南
android·开发语言·前端·python·php·lua