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

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

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

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

相关推荐
mldong5 小时前
你的 Vue3 项目也能有钉钉同款审批流设计器:npm 装包,10 分钟画出第一条审批流
前端·vue.js
2分钟速写快排5 小时前
什么是 RAG?如何用 RAG 实现一个用户记忆?
前端·后端·ai编程
passerby60616 小时前
如何自己造一个时间处理库
前端·javascript·github
走到天涯海角7 小时前
react里面的长列表渲染优化
前端·react.js·前端框架
小羊没烦恼!7 小时前
Hello Web API系列教程——Web API与国际化
java·服务器·前端·javascript·php
北岛贰8 小时前
迷茫焦虑期,我做了一个带支付带官网的 AI 聊天虚拟恋人 App
前端·人工智能·后端
mayaairi9 小时前
Vue2 组件通讯(三):全局事件总线、PubSub、插槽与组件实例属性
前端·javascript·vue.js
kyriewen10 小时前
面试官问我:AI 都能写代码了,前端凭什么还值 25K
前端·javascript·人工智能
风骏时光牛马10 小时前
AI源码分析:拆解模型底层实现逻辑
前端
IT_陈寒11 小时前
React子组件莫名其妙重渲染?你可能漏了这个Hook
前端·人工智能·后端