Skip to content

表单输入

分类Composition API(Vue3)Options API(Vue3)Options API(Vue2)
双向绑定-v-model v3.0v-model v2.0
-defineModel() v3.4
useModel() v3.4
-model v2.2

大纲

  • 表单输入
    • 表单控件
      • 属性
      • 事件
    • 绑定
      • 双向绑定
      • 自定义绑定
        • 参数
        • 表单修饰符
        • 多根节点双向绑定
    • 体验优化
      • 乐观的更新表单

表单控件

表单控件说明
<input>输入类型:textsearchnumberpasswordhiddenemailtelurl;选择类型:radiocheckboxfilecolormonthweekdatetimedatetime-localdatetime;拖动类型:range;按钮类型:buttonsubmitresetimage
<select>多选属性:multiple;分组标签:optgroup、分组名属性:label;选项标签:option
<textarea>行属性:rows;列属性:cols

双向绑定

v-model 可以自定义表单控件值属性及默认事件的行为。在表单控件或者组件上创建 双向绑定

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

  • input[text]textarea 元素使用 value 属性和 input 事件
  • input[checkbox]input[radio] 使用 checked 属性和 change 事件
  • select 元素使用 value 属性和 change 事件
vue
<script setup lang="ts">
// 父级
const title = ref('title');

// 子级
const model = defineModel()
</script>

<template>
  <!-- 父级 -->
   <CustomInput v-model="title" />
  
  <!-- 子级:CustomInput -->
   <input tye="text" v-model="model" />
   <!-- 类似于上面写法 -->
   <input tye="text" :value="modelValue" @input="$emit('update:modelValue', $event.target.value)" />
</template>
vue
<script lang="ts">
export default {
  // 父级
  data() {
    return {
      title: 'title'
    }
  },

  // 子级:CustomInput
  props: ['modelValue'],
  emits: ['update:modelValue']
}
</script>

<template>
  <!-- 父级 -->
  <CustomInput v-model="title" />
  
  <!-- 子级:CustomInput -->
  <input
    type="text"
    :value="modelValue"
    @input="$emit('update:modelValue', $event.target.value)"
  />
</template>

表单修饰符

表单修饰符说明
.lazy取代 input 监听 change 事件
.number输入字符串转为有效的数字
.trim输入首尾空格过滤

自定义双向绑定

如下示例所示:父级状态(count)和子级模型(defineModel 创建)通过 v-model 创建数据双向绑定。

vue
<script setup lang="ts">
// 父级
import { ref } from 'vue';
const count = ref(10);

// 子级:CustomInput
const model = defineModel()
function update() {
  model.value++
}
</script>

<template>
  <!-- 父级 -->
   <CustomInput v-model="count" />
  <button @click="count++">增加</button>
  
  <!-- 子级:CustomInput -->
  <div>Parent bound v-model is: {{ model }}</div>
  <button @click="update">CustomInput 增加</button>
</template>
vue
<script lang="ts">
export default {
  // 父级
  data() {
    return {
      count: 10
    }
  },
  // 子级:CustomInput
  props: ['modelValue'],
  emits: ['update:modelValue'],
  computed: {
    model: {
      get() {
        return this.modelValue
      },
      set(value) {
        this.$emit('update:modelValue', value)
      }
    }
  }
}
</script>

<template>
  <!-- 父级 -->
  <CustomInput v-model="count" />
  <button @click="count++">增加</button>
  
  <!-- 子级:CustomInput -->
  <div>Parent bound v-model is: {{ model }}</div>
  <!-- <input type="text" :value="modelValue" @input="$emit('update:modelValue', $event.target.value)" /> -->
  <!-- <input type="text" v-model="model" /> -->
  <button @click="update">CustomInput 增加</button>
</template>

使用 useModel(),通过 v-model 也可实现双向绑定。

useModel() 可以用于非 SFC 组件。与 defineModel() 不同的是,需要自己手动管理 props 和 emits。

vue
<script setup lang="ts">
// 父级
import { ref } from 'vue';
const count = ref(10);

// 子级:CustomInput
import { useModel } from 'vue'
interface Props {
  modelValue: number // 通过 ?: 校验是否必填项,string 校验类型
}
const props = withDefaults(defineProps<Props>(), {
  modelValue: 0 // 通过 withDefaults 第二个参数设置默认值
})
const model = useModel(props, 'modelValue')

function update() {
  model.value++
}
</script>

<template>
  <!-- 父级 -->
   <CustomInput v-model="count" />
  <button @click="count++">增加</button>
  
  <!-- 子级:CustomInput -->
  <div>Parent bound v-model is: {{ model }}</div>
  <button @click="update">CustomInput 增加</button>
</template>
vue
<script lang="ts">
export default {
  // 父级
  data() {
    return {
      count: 10
    }
  },
  // 子级:CustomInput
  props: {
    modelValue: Number
  },
  setup(props) {
    const model = useModel(props, 'modelValue')

    return {
      model
    }
  }
}
</script>

<template>
  <!-- 父级 -->
  <CustomInput v-model="count" />
  <button @click="count++">增加</button>
  
  <!-- 子级:CustomInput -->
  <div>Parent bound v-model is: {{ model }}</div>
  <button @click="update">CustomInput 增加</button>
</template>

Vue2 方案:

自定义绑定参数

v-model 可以接受一个参数,利用这个特性可以在单个组件实例上创建多个绑定。

vue
<script setup lang="ts">
// 父级
const first = ref('first');
const last = ref('last');

// 子级
const firstName = defineModel('firstName')
const lastName = defineModel('lastName')
</script>

<template>
  <!-- 父级 -->
   <CustomInput v-model:first-name="first" v-model:last-name="last" />
  
  <!-- 子级:CustomInput -->
   <input tye="text" v-model="firstName" />
   <input tye="text" :value="firstName" @input="$emit('update:firstName', $event.target.value)" />
   <input tye="text" v-model="lastName" />
   <input tye="text" :value="lastName" @input="$emit('update:lastName', $event.target.value)" />
</template>
vue
<script lang="ts">
export default {
  // 父级
  data() {
    return {
      first: 'first',
      last: 'last'
    }
  },

  // 子级:CustomInput
  props: {
    firstName: String,
    lastName: String,
  },
  emits: ['update:firstName', 'update:lastName'],
}
</script>

<template>
  <!-- 父级 -->
  <CustomInput v-model:first-name="first" v-model:last-name="last" />
  
  <!-- 子级:CustomInput -->
  <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>

自定义绑定修饰符

vue
<script setup lang="ts">
// 父级
const title = ref('title');

// 子级
const [title, modifiers] = defineModel('title', { required: true })
</script>

<template>
  <!-- 父级 -->
   <CustomInput v-model:title.capitalize="title" />
  
  <!-- 子级:CustomInput -->
   <input tye="text" v-model="title" />
   <input tye="text" :value="title" @input="$emit('update:title', $event.target.value)" />
</template>
vue
<script lang="ts">
export default {
  // 父级
  data() {
    return {
      first: 'first',
      last: 'last'
    }
  },

  // 子级:CustomInput
  props: {
    firstName: String,
    lastName: String,
  },
  emits: ['update:firstName', 'update:lastName'],
}
</script>

<template>
  <!-- 父级 -->
  <CustomInput v-model:first-name="first" v-model:last-name="last" />
  
  <!-- 子级:CustomInput -->
  <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>

多根节点双向绑定

vue
<script setup lang="ts">
// 父级
const first = ref('first');
const last = ref('last');

// 子级
const firstName = defineModel('firstName')
const lastName = defineModel('lastName')
</script>

<template>
  <!-- 父级 -->
   <CustomInput v-model:first-name="first" v-model:last-name="last" />
  
  <!-- 子级:CustomInput -->
   <input tye="text" v-model="firstName" />
   <input tye="text" :value="firstName" @input="$emit('update:firstName', $event.target.value)" />
   <input tye="text" v-model="lastName" />
   <input tye="text" :value="lastName" @input="$emit('update:lastName', $event.target.value)" />
</template>

Vue2 方案:

体验优化

参考