双向绑定 v-model

v-model 是 Vue 中最常用的指令之一,用于实现表单元素的双向数据绑定。它让数据与视图保持同步,大大简化了表单处理的代码量。

什么是双向绑定

双向绑定包含两个方向的数据流:

  1. 数据到视图:数据变化时,自动更新视图
  2. 视图到数据:用户操作视图时,自动更新数据
<div id="app">
  <input v-model="message">
  <p>你输入的是:{{ message }}</p>
</div>

<script>
new Vue({
  el: '#app',
  data: {
    message: 'Hello'
  }
})
</script>

当你在输入框中修改内容时,下方的文字会实时更新。当你在代码中修改 message 时,输入框的值也会同步变化。

v-model 的原理

v-model 本质上是语法糖,它等价于:

<input 
  v-bind:value="message"
  v-on:input="message = $event.target.value"
>

简写形式:

<input 
  :value="message"
  @input="message = $event.target.value"
>

理解这个原理很重要,因为:

  • 可以在自定义组件上实现 v-model
  • 遇到问题时知道如何调试
  • 理解 Vue 的响应式机制

不同表单元素的绑定

Vue 会根据表单元素类型自动选择正确的属性和事件:

文本输入框

<input type="text" v-model="text">
<input type="password" v-model="password">
<input type="email" v-model="email">
<input type="number" v-model="number">

绑定 value 属性,监听 input 事件。

多行文本

<textarea v-model="content"></textarea>

注意:在 textarea 中使用插值语法不会生效:

<!-- ❌ 错误:插值不会生效 -->
<textarea>{{ content }}</textarea>

<!-- ✅ 正确:使用 v-model -->
<textarea v-model="content"></textarea>

复选框

<!-- 单个复选框:绑定布尔值 -->
<input type="checkbox" v-model="checked">

<!-- 多个复选框:绑定数组 -->
<input type="checkbox" v-model="selected" value="apple"> 苹果
<input type="checkbox" v-model="selected" value="banana"> 香蕉
<input type="checkbox" v-model="selected" value="orange"> 橙子

绑定 checked 属性,监听 change 事件。

单选框

<input type="radio" v-model="picked" value="one"> 选项一
<input type="radio" v-model="picked" value="two"> 选项二

绑定 checked 属性,监听 change 事件。

下拉框

<select v-model="selected">
  <option value="a">选项 A</option>
  <option value="b">选项 B</option>
</select>

绑定 value 属性,监听 change 事件。

绑定静态值

对于单选框和复选框,可以使用静态值:

<!-- 单选框:选中时 picked = 'yes' -->
<input type="radio" v-model="picked" value="yes">

<!-- 复选框:选中时 checked = true,未选中时 checked = false -->
<input type="checkbox" v-model="checked">

<!-- 复选框:选中时 status = 'agree',未选中时 status = 'disagree' -->
<input 
  type="checkbox" 
  v-model="status"
  true-value="agree"
  false-value="disagree"
>

true-value 和 false-value

复选框默认绑定布尔值,但可以通过 true-valuefalse-value 自定义:

<div id="app">
  <input 
    type="checkbox" 
    v-model="status"
    true-value="yes"
    false-value="no"
  >
  <p>状态:{{ status }}</p>
</div>

<script>
new Vue({
  el: '#app',
  data: {
    status: 'no'  // 初始值
  }
})
</script>

选中时 status'yes',未选中时为 'no'

注意

true-valuefalse-value 不会影响输入框的 value 属性,只影响 v-model 绑定的值。

动态绑定值

可以使用 v-bind 动态绑定值:

<input 
  type="radio" 
  v-model="picked" 
  :value="dynamicValue"
>

<input 
  type="checkbox" 
  v-model="selected"
  :true-value="dynamicTrue"
  :false-value="dynamicFalse"
>

<script>
new Vue({
  data: {
    dynamicValue: { id: 1, name: '动态值' },
    dynamicTrue: 'yes',
    dynamicFalse: 'no'
  }
})
</script>

修饰符

Vue 提供了三个常用的修饰符:

.lazy

默认情况下,v-modelinput 事件中同步输入框的值。使用 .lazy 修饰符可以改为在 change 事件后同步:

<!-- 默认:每次输入都同步 -->
<input v-model="text">

<!-- lazy:失去焦点或回车后才同步 -->
<input v-model.lazy="text">

适用场景:

  • 表单验证(不需要实时验证)
  • 中文输入法(避免拼音输入时触发)

.number

自动将用户输入转换为数字类型:

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

如果输入值无法被 parseFloat() 解析,则返回原始值。

适用场景:

  • 年龄、数量等数字输入
  • 避免字符串类型的数字比较问题

.trim

自动去除首尾空格:

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

适用场景:

  • 用户名、搜索关键词等
  • 避免用户不小心输入空格

组合使用

修饰符可以组合使用:

<input v-model.lazy.trim="text">
<input v-model.number.trim="price">

v-model 在组件上的使用

v-model 也可以用在自定义组件上,但需要在组件内部正确实现:

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

<!-- 子组件 -->
<template>
  <input 
    :value="value"
    @input="$emit('input', $event.target.value)"
  >
</template>

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

这部分内容在组件通信章节会详细讲解。

实战示例

搜索框

<div class="search-box">
  <input 
    type="text" 
    v-model.trim="keyword"
    @keyup.enter="search"
    placeholder="搜索..."
  >
  <button @click="search">搜索</button>
</div>

<script>
new Vue({
  data: {
    keyword: ''
  },
  methods: {
    search: function() {
      if (!this.keyword) return
      console.log('搜索:', this.keyword)
    }
  }
})
</script>

实时计算

<div class="calculator">
  <input type="number" v-model.number="a"> +
  <input type="number" v-model.number="b">
  = {{ a + b }}
</div>

<script>
new Vue({
  data: {
    a: 0,
    b: 0
  }
})
</script>

表单重置

<form @submit.prevent="submit">
  <input v-model="form.name" placeholder="姓名">
  <input v-model="form.email" placeholder="邮箱">
  <button type="submit">提交</button>
  <button type="button" @click="reset">重置</button>
</form>

<script>
new Vue({
  data: {
    form: {
      name: '',
      email: ''
    },
    defaultForm: {
      name: '',
      email: ''
    }
  },
  methods: {
    submit: function() {
      console.log('提交:', this.form)
    },
    reset: function() {
      this.form = Object.assign({}, this.defaultForm)
    }
  }
})
</script>

小结

v-model 是 Vue 表单处理的核心:

  1. 语法糖:本质是 :value + @input 的组合
  2. 智能绑定:根据元素类型自动选择正确的属性和事件
  3. 修饰符.lazy.number.trim 简化常见操作
  4. 组件支持:可以在自定义组件上使用 v-model

掌握 v-model,你就能轻松处理各种表单场景。