Vue2 和 Vue3 中 v-model 指令的区别

279 阅读1分钟

v-model 指令

首先需要理解,v-model 本质上是语法糖,在代码背后,模板编译器会对 v-model 进行更冗长的等价展开。

形如

<input v-model="searchText" />

等价于

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

所以 v-model 做的事情就是绑定一个响应式变量,并监听一个事件用来更新这个变量的值,因而 v-model 在使用上会有一些限制:

  • 限制

    • <input><textarea>
    • <select>
    • <checkbox><radio>
    • components
  • 修饰符

    • .lazy - 监听 change 事件而不是 input
    • .number - 输入字符串转为有效的数字
    • .trim - 输入首尾空格过滤
  • 用法

    表单控件或者组件上创建双向绑定。

    表单控件上可以使用 v-model 指令在表单 <input><textarea> 及 <select> 等元素上创建双向数据绑定。它会根据控件类型自动选取正确的方法来更新元素。因为 v-model 本质上不过是语法糖,v-model 在内部为不同的输入元素使用不同的 property 并抛出不同的事件:

    • text 和 textarea 元素使用 value property 和 input 事件;
    • checkbox 和 radio 使用 checked property 和 change 事件;
    • select 字段将 value 作为 prop 并将 change 作为事件。

    组件上的使用稍有不同

v-model 在 Vue2 中的使用

在 Vue 2.0+ 的版本中,一个组件上的 v-model 默认会利用名为 value 的 prop 和名为 input 的事件,但是像自定义单选框、复选框等类型的封装输入控件可能会将 value attribute 用于不同的目的。为了解决这个问题,Vue 在 Vue 2.2 中引入了 model 选项,可以自定义要接受的属性名自定义事件名

Vue.component('base-checkbox', {  
  model: {  
    prop: 'checked',  
    event: 'change'  
  ,  
  props: {  
    checked: Boolean  
  },  
  template: `  
    <input  
      type="checkbox"  
      v-bind:checked="checked"  
      v-on:change="$emit('change', $event.target.checked)"  
    >  
`  
})

组件上还是这样使用

<base-checkbox v-model="isChecked"></base-checkbox>

v-model 在 Vue3 中的使用

Vue2.x 中因为 v-model 默认的编译展开为 value 的 prop 和 input 事件可能会发生冲突,那么可不可以直接使用另外的属性和方法呢?其实 Vue3 中就实现了这个功能,v-model绑定的不再是value,而是modelValue,接收的方法也不再是input,而是update:modelValue

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

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

<CustomInput> 组件内部则需要做两件事:

  1. 将内部原生 <input> 元素的 value attribute 绑定到 modelValue prop
  2. 当原生的 input 事件触发时,触发一个携带了新值的 update:modelValue 自定义事件
<!-- CustomInput.vue -->
<script>
export default {
  props: ['modelValue'],
  emits: ['update:modelValue']
}
</script>

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

组件上的使用方式还是没变:

<CustomInput v-model="searchText" />

并且 Vue3 中的 v-model 指令还能支持参数多个绑定

v-model 支持参数

默认情况下,v-model 在组件上都是使用 modelValue 作为 prop,并以 update:modelValue 作为对应的事件。我们可以通过给 v-model 指定一个参数来更改这些名字:

<MyComponent v-model:title="bookTitle" />

在这个例子中,子组件应声明一个 title prop,并通过触发 update:title 事件更新父组件值:

<!-- MyComponent.vue -->
<script>
export default {
  props: ['title'],
  emits: ['update:title']
}
</script>

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

v-model 支持多个绑定

Vue3 支持在单个组件实例上创建多个 v-model 双向绑定。组件上的每一个 v-model 都会同步不同的 prop,而无需额外的选项:

<UserName
  v-model:first-name="first"
  v-model:last-name="last"
/>
<script>
export default {
  props: {
    firstName: String,
    lastName: String
  },
  emits: ['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>