v-model在vue2与vue3中的区别

本文详细解析Vue框架中v-model指令的工作原理及其在不同版本Vue中的应用差异,包括基本用法、修饰符及组件中多属性绑定的实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

v-model主要是用在表单 <input>、<textarea> 、<select> 元素上创建双向数据绑定。它会根据控件类型自动选取正确的方法来更新元素。尽管有些神奇,但 v-model 本质上不过是语法糖。它负责监听用户的输入事件以更新数据。

v-model 会忽略所有表单元素的 value、checked、selected attribute 的初始值而总是将 Vue 实例的数据作为数据来源。你应该通过 JavaScript 在组件的 data 选项中声明初始值。

v-model 在内部为不同的输入元素使用不同的 property 并抛出不同的事件:

  1. text 和 textarea 元素使用 value property 和 input 事件
  2. checkbox 和 radio 元素使用 checked property 和 change 事件
  3. select 元素使用 value property 和 change 事件

注意:v-model 不会在输入法组合文字过程中得到更新。如果你也想处理这个过程,请使用 input 事件

v-model在vue2和vue3的区别

相同点

  1. 用在表单上都是给input绑定一个 value 值和 input 事件(默认情况)

不同点(主要用在组件上)

用在组件上(vue2)

  • v-model 只能使用一次
  • 默认传递 vulue属性,接受 input 事件
  • 默认传递的属性和事件可通过 model 选项进行修改

用在组件上(vue3)

  • v-model 只能使用一次,但可以在v-model后添加参数可使用多次
  • 默认传递 modelValue 属性,接受 update:modelValue 事件,当在v-model后添加参数时如v-model:myPropName,则传递为 myPropName 属性,接受 update:myPropName事件
  • 默认传递的属性和事件无法修改,必须是modelValue 和 update:modelValue

vue2中 .sync 与 v-model的区别

相同点

  • 都是语法糖,都可以实现父子组件数据的双向绑定

不同点

  • v-model 背后是 value 属性 和 input 事件
  • myPropName.sync 背后是 myPropName 属性 和 update:myPropName 事件
  • 在组件上 v-model 只可使用一次,.sync可以有多个

注意:vue3中去除了 .sync修饰符,但把相应功能合并到了 v-model 上,所以在 vue3 中,v-model 可以使用多次

v-model 简单解析

<input type="text" v-model="searchText">

等同于
<input
  :value="searchText"
  @input="searchText = $event.target.value"
/>

v-model用在<input type=‘radio’ />上

  <label for="man">男</label>
  <input type="radio" id="man" value="Man" v-model="sex" />
  <label for="woman">女</label>
  <input type="radio" id="woman" value="Woman" v-model="sex" />

  <!-- 等同于上面 -->
  <label for="man">男</label>
  <input
    type="radio"
    id="man"
    value="Man"
    :checked="sex === 'Man'"
    @change="handleSexChange"
  />

  <label for="woman">女</label>
  <input
    type="radio"
    id="woman"
    value="Woman"
    :checked="sex === 'Woman'"
    @change="handleSexChange"
  />

  handleSexChange (event) {
    this.sex = event.target.value
  }

v-model在 vue2 和 vue3 组件上的具体区别

v-model用在 vue2 组件

<!-- Test.vue -->
<CustomInput v-model="searchText" />

等同于
<CustomInput
  :value="searchText"
  @input="newValue => searchText = newValue"
/>
<!-- CustomInput.vue -->
<template>
  <input type="text" :value="value" @input="handleInput($event)" />
</template>
<script>
export default {
  props: {
    value: String
  },
  methods: {
    handleInput (event) {
      this.$emit('input', event.target.value)
    }
  }
}
</script>
  • 在 CustomInput.vue中 props内必须定义value属性,否则input元素无法使用
  • 在 CustomInput.vue中,需要向外派发 input 事件去更新 value 值

如果我们想在 CustomInput.vue 中使用v-model,则需要定义同时具有 getter 和 setter 的计算属性。get 方法需返回 value prop,而 set 方法需触发相应的事件


<!-- Test.vue -->
<CustomInput v-model="searchText"></CustomInput>

<!-- CustomInput.vue -->
<template>
  <input type="text" v-model="customValue" />
</template>
<script>
export default {
  props: {
    value: String
  },
  computed: {
    customValue: {
      get () {
        return this.value
      },
      set (value) {
        this.$emit('input', value)
      }
    }
  }
}
</script>

model的使用

如果在 CustomInput.vue 中我们不想使用默认 props 里的 value 名和 触发 input事件,那我们可以使用 model 选项,它可以通过 prop 自定义 value 字段名并通过 event 自定义触发的 事件名


<!-- Test.vue -->
<CustomInput v-model="searchText"></CustomInput>

<!-- CustomInput.vue -->
<template>
  <div>
   <input type="text" :value="customValue" @input="handleInput($event)" />
  </div>
 </template>
<script>
export default {
  model: {
    prop: 'customValue',
    event: 'customInput'
  },
  props: {
    customValue: String
  },
  methods: {
    handleInput (event) {
      this.$emit('customInput', event.target.value)
    }
  }
}
</script>

注意:虽然我们可以在 model 中自定义任何名字,但像上面的这种写法并没有什么语义,model的出现是为了解决像单选框、复选框等类型的输入控件可能会将 value 属性用于不同的目的,使用model选项是为了避免这样的冲突,让代码更有语义

checkBox使用示例

<!-- Test.vue -->
<CustomInput v-model="isShow"></CustomInput>

<!-- CustomInput.vue -->
<template>
   <input type="checkbox" :checked="checked" @change="handleChange($event)" />
 </template>
<script>
export default {
  model: {
    prop: 'checked',
    event: 'change'
  },
  props: {
    checked: Boolean
  },
  methods: {
    handleChange (event) {
      this.$emit('change', event.target.checked)
    }
  }
}
</script>

这里 isShow 的值将会传入这个名为 checked 的prop。同时当 <CustomInput/> 触发一个 change 事件并附带一个新值时,这个 isShow 也将会被更新

注意:

  • 你仍然需要在组件的 props 选项里声明 checked 这个 prop
  • 不管属性和事件是什么,v-model都需要绑定固定的属性和事件,这也就导致了组件上只能使用一次v-model,但并不是所有元素都有 input、change事件,而且如果我们想对多个 props 中的值进行 双向绑定 又该怎么做呢?

.sync 修饰符

sync 也是语法糖

<text-document v-bind:title.sync="doc.title"></text-document>

等同于
<text-document
  v-bind:title="doc.title"
  v-on:update:title="doc.title = $event"
></text-document>

从代码可以看出,它和v-model的实现原理几乎一样,只是属性和事件名和v-model不同罢了。

注意:在 sync 中我们需要使用 update:myPropName 的模式触发事件,因 myPropName 属性名称不是固定的,所以事件也就不是固定的,因此 .sync 修饰符可以在组件上多次使用,此时也就支持了对多个 props 属性的 双向绑定

<!-- Test.vue -->
<CustomInput :firstName.sync="obj.firstName" :lastName.sync="obj.lastName"></CustomInput>

<!-- CustomInput.vue -->
<template>
  <div>
    <div><span>{{firstName}}</span><span @click="changeX">更改姓</span></div>
    <div><span>{{lastName}}</span><span @click="changeM">更改名</span></div>
  </div>
 </template>
<script>
export default {
  props: {
    firstName: String,
    lastName: String
  },
  methods: {
    changeX () {
      this.$emit('update:firstName', 'liu')
    },
    changeM () {
      this.$emit('update:lastName', 'yz')
    }
  }
}
</script>

v-model用在vue3组件

默认情况下 vue2 的 v-model 用在表单上 和 用在组件上 表现形式是一样的。都是 :value 和 @input,但在vue3上 v-model的表现就不一样了 用在表单上是:

 <input v-model="searchText" />
 等同于
 <input
  :value="searchText"
  @input="searchText = $event.target.value"
/>

而当使用在一个组件上时,v-model 会被展开为如下的形式:

<CustomInput
  :modelValue="searchText"
  @update:modelValue="newValue => searchText = newValue"
/>

用在组件上我们可以看到,传递给组件的属性变成了 modelValue ,接受的事件变成了 update:modelValue

  <!-- Test.vue -->
  <CustomInput v-model="searchText" />
  
  <!-- CustomInput.vue -->
  <script setup>
  defineProps(['modelValue'])
  defineEmits(['update:modelValue'])
  </script>
  
  <template>
    <input
      :value="modelValue"
      @input="$emit('update:modelValue', $event.target.value)"
    />
  </template>

当我们在组件上使用 v-model 时,它的 modelValue 和 update:modelValue 都已固定,不可修改,它并没有像 vue2 中的 model 来修改这些变量名。那在vue3的组件上如何实现 多个props 属性的双向绑定呢?先不急,我们先看看v-model 的另一种用法

另一种在组件内实现 v-model 的方式是使用一个可写的,同时具有 getter 和 setter 的计算属性。get 方法需返回 modelValue prop,而 set 方法需触发相应的事件:

  <!-- Test.vue -->
  <CustomInput v-model="searchText" />
  
  <!-- CustomInput.vue -->
  <template>
    <input v-model="value" />
  </template>
  
  <script setup>
  import { computed } from 'vue'
  
  const props = defineProps(['modelValue'])
  const emit = defineEmits(['update:modelValue'])
  
  const value = computed({
    get() {
      return props.modelValue
    },
    set(value) {
      emit('update:modelValue', value)
    }
  })
  </script>

让 props 上的多个属性实现双向绑定我们首先想到的是 .sync修饰符,但是很遗憾,vue3已经去除了 .sync修饰符,同时把 .sync 的功能都集成到 v-model上。默认情况下,v-model在组件上都是使用 modelValue 作为 prop,并以 update:modelValue 作为对应的事件。我们可以通过给 v-model 指定一个参数来更改这些名字:

在下面这个例子中,子组件应声明 firstName 和 lastName 两个prop,并通过触发 update:firstName 和 update:lastName 事件更新父组件值:

  <!-- Test.vue -->
  <MyComponent
    v-model:first-name="first"
    v-model:last-name="last"
  />
  
  <!-- MyComponent.vue -->
  defineProps({
    firstName: String,
    lastName: String
  })
  
  defineEmits(['update:firstName', 'update:lastName'])
  </script>
  
  <template>
    <input
      type="text"
      :value="firstName"
      @input="$emit('update:firstName', $event.target.value)"
    />
    <input
      type="text"
      :value="lastName"
      @input="$emit('update:lastName', $event.target.value)"
    />
  </template>

v-model 内置修饰符

v-model 有一些内置的修饰符,例如 .trim,.number 和 .lazy

.lazy

默认情况下,v-model 会在每次 input 事件后更新数据 (拼字阶段不更新)。你可以添加 lazy 修饰符来改为在每次 change 事件后更新数据:

  template
  <!-- 在 "change" 事件后同步更新而不是 "input" -->
  <input v-model.lazy="msg" />

.number

如果你想让用户输入自动转换为数字,你可以在 v-model 后添加 .number 修饰符来管理输入:

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

如果该值无法被 parseFloat() 处理,那么将返回原始值。如: “12ab” => 12, “12a34” => 12,“012” => 12,“ab” => ‘ab’ “ab12” => “ab12”
number 修饰符会在输入框有 type=“number” 时自动启用。
注意:如果值可以被 parseFloat处理则 typeof 返回的类型为 number, 否则返回 string

.trim

如果你想要默认自动去除用户输入内容中两端的空格,你可以在 v-model 后添加 .trim 修饰符:

  <input v-model.trim="msg" />

v-model 自定义修饰符(vue3)

  <!-- Test.vue -->
  <MyComponent v-model.capitalize="myText" />

组件的 v-model 上所添加的修饰符,可以通过 modelModifiers prop 在组件内访问到。在下面的组件中,我们声明了 modelModifiers 这个 prop,它的默认值是一个空对象:

  <!-- MyComponent.vue -->
  <script setup>
  const props = defineProps({
    modelValue: String,
    modelModifiers: { default: () => ({}) }
  })

  defineEmits(['update:modelValue'])

  console.log(props.modelModifiers) // { capitalize: true }
  </script>

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

注意:这里组件的 modelModifiers prop 包含了 capitalize 且其值为 true,因为它在模板中的 v-model 绑定上被使用了

有了 modelModifiers 这个 prop,我们就可以在原生事件侦听函数中检查它的值,然后决定触发的自定义事件中要向父组件传递什么值。在下面的代码里,我们就是在每次 元素触发 input 事件时将值的首字母大写:

  <!-- MyComponent.vue -->
  <script setup>
  const props = defineProps({
    modelValue: String,
    modelModifiers: { default: () => ({}) }
  })

  const emit = defineEmits(['update:modelValue'])

  function emitValue(e) {
    let value = e.target.value
    if (props.modelModifiers.capitalize) {
      value = value.charAt(0).toUpperCase() + value.slice(1)
    }
    emit('update:modelValue', value)
  }
  </script>

  <template>
    <input type="text" :value="modelValue" @input="emitValue" />
  </template>

对于又有参数又有修饰符的 v-model 绑定,生成的 prop 名将是 arg + “Modifiers”。举例来说:

  <MyComponent v-model:title.capitalize="myText">
  相应的声明应该是:

  const props = defineProps(['title', 'titleModifiers'])
  defineEmits(['update:title'])

  console.log(props.titleModifiers) // { capitalize: true }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值