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

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

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

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

相关推荐
信看17 小时前
NMEA-GNSS-RTK 定位html小工具
前端·javascript·html
Tony Bai17 小时前
【API 设计之道】04 字段掩码模式:让前端决定后端返回什么
前端
苏打水com18 小时前
第十四篇:Day40-42 前端架构设计入门——从“功能实现”到“架构思维”(对标职场“大型项目架构”需求)
前端·架构
king王一帅18 小时前
流式渲染 Incremark、ant-design-x markdown、streammarkdown-vue 全流程方案对比
前端·javascript·人工智能
苏打水com18 小时前
第十八篇:Day52-54 前端跨端开发进阶——从“多端适配”到“跨端统一”(对标职场“全栈化”需求)
前端
Bigger18 小时前
后端拒写接口?前端硬核自救:纯前端实现静态资源下载全链路解析
前端·浏览器·vite
BD_Marathon18 小时前
【JavaWeb】路径问题_前端绝对路径问题
前端
whyfail19 小时前
Vue原理(暴力版)
前端·vue.js