阅读视图

发现新文章,点击刷新页面。

Vue v-model 指令详解

什么是 v-model?

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

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

基本用法

在原生表单元素上使用

<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 事件(在输入完成时更新)

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

2. .number

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

<input v-model.number="age" type="number">

3. .trim

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

<input v-model.trim="username">

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

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

Vue 2 的实现方式

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

<!-- 父组件 -->
<CustomInput v-model="message" />

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

子组件实现:

<template>
  <input
    :value="value"
    @input="$emit('input', $event.target.value)"
  >
</template>

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

Vue 3 的实现方式

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

<!-- 父组件 -->
<CustomInput v-model="message" />

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

子组件实现:

<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:

<UserForm
  v-model:first-name="firstName"
  v-model:last-name="lastName"
  v-model:email="email"
/>

子组件实现:

<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>

自定义修饰符

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

<CustomInput v-model.capitalize="message" />

子组件实现:

<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 本质上是语法糖,它结合了属性绑定和事件监听:

<input v-model="searchText">

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

对于组件:

<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 可以创建高度可复用的表单组件,提升开发效率。

❌