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

相关推荐
TE-茶叶蛋10 分钟前
Flutter、Vue 3 和 React 在 UI 布局比较
vue.js·flutter·react.js
遇到困难睡大觉哈哈15 分钟前
CSS中的Element语法
前端·css
Real_man21 分钟前
新物种与新法则:AI重塑开发与产品未来
前端·后端·面试
小彭努力中21 分钟前
147.在 Vue3 中使用 OpenLayers 地图上 ECharts 模拟飞机循环飞行
前端·javascript·vue.js·ecmascript·echarts
老马聊技术24 分钟前
日历插件-FullCalendar的详细使用
前端·javascript
咔咔一顿操作26 分钟前
Cesium实战:交互式多边形绘制与编辑功能完全指南(最终修复版)
前端·javascript·3d·vue
LuckyLay2 小时前
使用 Docker 搭建 Rust Web 应用开发环境——AI教你学Docker
前端·docker·rust
pobu1682 小时前
aksk前端签名实现
java·前端·javascript
烛阴2 小时前
带参数的Python装饰器原来这么简单,5分钟彻底掌握!
前端·python
0wioiw02 小时前
Flutter基础(前端教程⑤-组件重叠)
开发语言·前端·javascript