Vue v-model 指令详解

什么是 v-model?

v-model 是 Vue 中最常用的指令之一,它实现了表单输入元素与 Vue 实例数据的双向绑定。这意味着:

  • 当用户修改表单元素的值时,Vue 实例的数据会自动更新
  • 当 Vue 实例数据变化时,表单元素的值也会自动更新

基本用法

在原生表单元素上使用

html 复制代码
<template>
  <div class="container">
    <!-- 文本输入 -->
    <div class="form-group">
      <label>用户名:</label>
      <input v-model="username" placeholder="输入用户名">
      <p>当前值:{{ username }}</p>
    </div>
    
    <!-- 多行文本 -->
    <div class="form-group">
      <label>个人简介:</label>
      <textarea v-model="bio" placeholder="输入个人简介"></textarea>
      <p class="preview">预览:{{ bio }}</p>
    </div>
    
    <!-- 复选框 -->
    <div class="form-group">
      <label>
        <input type="checkbox" v-model="agreed"> 我同意服务条款
      </label>
      <p v-if="agreed" class="success">已同意条款</p>
    </div>
    
    <!-- 单选按钮 -->
    <div class="form-group">
      <label>选择性别:</label>
      <div class="radio-group">
        <label>
          <input type="radio" value="male" v-model="gender"> 男性
        </label>
        <label>
          <input type="radio" value="female" v-model="gender"> 女性
        </label>
        <label>
          <input type="radio" value="other" v-model="gender"> 其他
        </label>
      </div>
      <p>选择结果:{{ gender }}</p>
    </div>
    
    <!-- 下拉选择 -->
    <div class="form-group">
      <label>选择城市:</label>
      <select v-model="city">
        <option disabled value="">请选择</option>
        <option value="beijing">北京</option>
        <option value="shanghai">上海</option>
        <option value="guangzhou">广州</option>
        <option value="shenzhen">深圳</option>
      </select>
      <p>所选城市:{{ city }}</p>
    </div>
  </div>
</template>

<script>
export default {
  data() {
    return {
      username: '',
      bio: '',
      agreed: false,
      gender: '',
      city: ''
    }
  }
}
</script>

<style>
.container {
  max-width: 600px;
  margin: 0 auto;
  padding: 20px;
  font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
}

.form-group {
  margin-bottom: 25px;
  padding: 15px;
  border-radius: 8px;
  background-color: #f8f9fa;
  box-shadow: 0 2px 5px rgba(0,0,0,0.1);
}

label {
  display: block;
  margin-bottom: 8px;
  font-weight: 600;
  color: #333;
}

input[type="text"], 
textarea, 
select {
  width: 100%;
  padding: 10px;
  border: 1px solid #ddd;
  border-radius: 4px;
  font-size: 16px;
  margin-bottom: 10px;
}

input[type="checkbox"], 
input[type="radio"] {
  margin-right: 8px;
}

.radio-group {
  display: flex;
  gap: 15px;
  margin: 10px 0;
}

.preview {
  white-space: pre-wrap;
  background-color: #fff;
  padding: 10px;
  border-radius: 4px;
  border-left: 3px solid #42b983;
}

.success {
  color: #42b983;
  font-weight: 600;
}

p {
  margin: 8px 0;
  color: #555;
}
</style>

v-model 修饰符

Vue 为 v-model 提供了几个有用的修饰符:

1. .lazy

将 input 事件转换为 change 事件(在输入完成时更新)

html 复制代码
<!-- 输入完成后才更新数据 -->
<input v-model.lazy="message">

2. .number

自动将用户输入转为数值类型

html 复制代码
<input v-model.number="age" type="number">

3. .trim

自动去除用户输入的首尾空白字符

html 复制代码
<input v-model.trim="username">

在自定义组件上使用 v-model

v-model 也可用于自定义组件,实现组件与父级数据的双向绑定:

Vue 2 的实现方式

在 Vue 2 中,组件上的 v-model 默认使用 value 属性和 input 事件:

html 复制代码
<!-- 父组件 -->
<CustomInput v-model="message" />

<!-- 等价于 -->
<CustomInput :value="message" @input="message = $event" />

子组件实现:

html 复制代码
<template>
  <input
    :value="value"
    @input="$emit('input', $event.target.value)"
  >
</template>

<script>
export default {
  props: ['value']
}
</script>

Vue 3 的实现方式

Vue 3 默认使用 modelValue 属性和 update:modelValue 事件:

html 复制代码
<!-- 父组件 -->
<CustomInput v-model="message" />

<!-- 等价于 -->
<CustomInput 
  :modelValue="message"
  @update:modelValue="message = $event"
/>

子组件实现:

html 复制代码
<template>
  <input
    :value="modelValue"
    @input="$emit('update:modelValue', $event.target.value)"
  >
</template>

<script>
export default {
  props: ['modelValue']
}
</script>

Vue 3 中的高级用法

多个 v-model 绑定

Vue 3 允许在单个组件上使用多个 v-model:

html 复制代码
<UserForm
  v-model:first-name="firstName"
  v-model:last-name="lastName"
  v-model:email="email"
/>

子组件实现:

html 复制代码
<template>
  <input :value="firstName" @input="$emit('update:firstName', $event.target.value)">
  <input :value="lastName" @input="$emit('update:lastName', $event.target.value)">
  <input :value="email" @input="$emit('update:email', $event.target.value)">
</template>

<script>
export default {
  props: ['firstName', 'lastName', 'email'],
  emits: ['update:firstName', 'update:lastName', 'update:email']
}
</script>

自定义修饰符

可以为自定义组件创建特定的修饰符:

html 复制代码
<CustomInput v-model.capitalize="message" />

子组件实现:

html 复制代码
<template>
  <input
    :value="modelValue"
    @input="emitValue($event.target.value)"
  >
</template>

<script>
export default {
  props: {
    modelValue: String,
    modelModifiers: {
      default: () => ({})
    }
  },
  methods: {
    emitValue(value) {
      if (this.modelModifiers.capitalize) {
        value = value.charAt(0).toUpperCase() + value.slice(1)
      }
      this.$emit('update:modelValue', value)
    }
  }
}
</script>

底层实现原理

v-model 本质上是语法糖,它结合了属性绑定和事件监听:

html 复制代码
<input v-model="searchText">

<!-- 等价于 -->
<input
  :value="searchText"
  @input="searchText = $event.target.value"
>

对于组件:

html 复制代码
<custom-input v-model="searchText"></custom-input>

<!-- 等价于 -->
<custom-input
  :model-value="searchText"
  @update:model-value="searchText = $event"
></custom-input>

最佳实践

  1. 表单验证:结合 v-model 和表单验证库(如 VeeValidate)
  2. 性能优化:对于复杂表单,考虑使用 .lazy 修饰符减少更新频率
  3. 组件设计:为自定义表单组件实现 v-model 接口
  4. 状态管理:在大型应用中,将表单状态存储在 Vuex 或 Pinia 中
  5. 无障碍访问:确保表单元素有正确的 label 和 aria 属性

总结

v-model 是 Vue 中处理表单数据的核心指令,它提供了简洁的双向绑定语法。通过理解其工作原理和各种修饰符,你可以更高效地处理表单交互。在自定义组件中使用 v-model 可以创建高度可复用的表单组件,提升开发效率。

相关推荐
卸任6 小时前
Vibe coding工具使用心得
前端·ai编程
brien97 小时前
为什么模型标称 1M,Google Antigravity 聊到 256K 却突然失忆?我们给它做了个原生行内监控圆环
前端
修炼的dance7 小时前
录屏关键帧间隔多少合适?六档实测,2秒到10秒小了75.6%
前端
Lank_M7 小时前
关键帧从1秒拉到10秒,录屏小87.3%,摄像头小10.8%
前端
补码缩写7 小时前
用 ffprobe 拆录屏,八成字节在 29 个关键帧上
前端
百思不得小李7 小时前
一个复杂的动态表单是如何实现的
前端·设计模式
陈随易7 小时前
Cmder 停更 17 个月,2.0 支持 Windows Terminal
前端·后端·程序员
风骏时光牛马7 小时前
前沿视界
前端
kyriewen8 小时前
我扫了 814 个依赖,97 个还敢要 Node 10
前端·javascript·程序员
陈随易8 小时前
3000 万人在用的翻译插件,重写一遍就是一门生意
前端·后端·程序员